文章来源: IMweb前端社区 黄qiong(imweb.io)
IMweb团队正在招聘啦,简历发至jayccchen@tencent.com前端
Redux 实际上是用来帮咱们管理状态的一个框架,它暴露给咱们四个接口,分别是:react
createStoreweb
combineReducersredux
bindActionCreators性能优化
applyMiddlewareapp
compose框架
源码系列里会分别对这五个接口进行解析。less
Redux 的源码解析系列开篇以前,先来了解一下它的实现思想。函数
假设一种场景下,app里每一个组件都须要拿到appState的一部分进行渲染。性能
可是这里存在一个风险就是,谁均可以修改appState的值,换句话说,有一天当appState变了你都不知道是谁改的,因此咱们须要有一个管理员来帮咱们管理咱们的状态,这时候引入了dispatch函数,来专门修改负责数据的修改。
function dispatch (action) { switch (action.type) { case 'UPDATE_TITLE_TEXT': appState.title.text = action.text break case 'UPDATE_TITLE_COLOR': appState.title.color = action.color break default: break } }
解决问题:
既能够解决组件共享问题,同时不会有数据能够被任意修改的问题。
如今咱们有了状态,又有了dispatch,这时候咱们须要一个高层管理者store,帮咱们管理好他们,这样再用的时候就能够直接store.getState store.dispatch的方式获取和更改组件。
因此咱们就有了createStore这个函数帮咱们生成store, 而后将getState 跟 dispatch 方法export出去。
function createStore(state, stateChanger) { const getState = () => state; const dispatch = (action) => stateChanger(state, action) return {getState, dispatch} }
createStore 接受两个参数,一个是表示app的 state。另一个是 stateChanger,它来描述应用程序状态会根据 action 发生什么变化,其实就是至关于本节开头的 dispatch 代码里面的内容,咱们后来会将它命名为reducer。
可是这里还有一个问题,就是数据发生改变以后,咱们都须要手动在从新render一次APP,这时候就须要观察者模式,订阅数据的改变,而后自动调用renderAPP,因此咱们的createStore功能又强大啦~
function createStore(state, reducer) { const getState = () => state; const listeners = []; const subscribe = (listener) => { listeners.push(listener) } const dispatch = (action) => { reducer(state, action); // 数据已发生改变就把全部的listener跑一遍 listeners.forEach((listener) => { listener() }) } return {getState, dispatch, subscribe} }
咱们就能够这样使用
store.subscribe(() => renderApp(store.getState()))
由此能够看出,dispatch是一个重要函数,当每一次咱们调用dispatch去改变app的状态的时候,它都会同时执行全部的订阅函数。
到这一步,一个APP就已经能够无压力的跑起来啦,最后一步,固然是关注性能,咱们这个app 仍是有严重性能问题的,由于每一次的dispatch 全部的子组件都会被从新渲染,这固然是没必要要的。
因此就须要对reducer产生的先后appState进行一个对比,这就要求reducer必须是一个纯函数,返回的是一个新的object,不能直接更改reducer的参数,这样才可以对比能够经过对比先后的state是否相等,来决定是否render
// reducer用来管理状态变化 function reducer (state, action) { if(!state) { return appState; } switch (action.type) { case 'CHANGE_TITLE': return { ...state, title: { ...state.title, text: action.text } } case 'CHANGE_CONTENT': return { ...state, content: { ...state.content, color: action.color } } } }
function createStore(state, reducer) { let appState = state; const getState = () => appState; const listeners = []; const subscribe = (listener) => { listeners.push(listener) } const dispatch = (action) => { // 覆盖原先的appState appState = reducer(state, action); listeners.forEach((listener) => { listener() }) } return {getState, dispatch, subscribe} }
OK,到这一步,咱们的redux就基本完成啦~ 接着改装下咱们的reducer,让它有一个初始值,这样咱们的createStore就只须要传入一个reducer便可
// reducer用来管理状态变化 function reducer (state, action) { //设置初始值 if(!state) { return appState; } switch (action.type) { case 'CHANGE_TITLE': return { ...state, title: { ...state.title, text: action.text } } case 'CHANGE_CONTENT': return { ...state, content: { ...state.content, color: action.color } } } }
function createStore (reducer) { let state = null const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { // 能够看到 因为reducer返回的是一个新的object,那在外层,咱们就能够对比nextProps跟t his.props 来决定是否渲染 state = reducer(state, action) listeners.forEach((listener) => listener()) } dispatch({}) // 初始化 state return { getState, dispatch, subscribe } }
总结如下:createStore里要作三件事
getState
dispatch
subscribe
初始reducer的状态
四个步骤
// 定一个 reducer, 负责管理数据变化还有初始化appState的数据 function reducer (state, action) { /* 初始化 state 和 switch case */ } // 生成 store const store = createStore(reducer) // 监听数据变化从新渲染页面 store.subscribe(() => renderApp(store.getState())) // 首次渲染页面 renderApp(store.getState()) // 后面能够随意 dispatch 了,页面自动更新 store.dispatch(...)
咱们整个过程就是不断地发现问题,解决问题
一、共享状态 -> dispatch
二、store统一管理 dispatch getState
三、性能优化 --> reducer是一个纯函数
四、最终初始化整个reducer
以上就是redux的大体思想。
参考文档: