美文网首页
redux 源码解读

redux 源码解读

作者: yeatszhang | 来源:发表于2016-06-17 22:27 被阅读61次

    前言

    redux并不局限于flux与react。redux 自身保持简洁以便适配各种场景,让社区发展出各种 redux-* 中间件或者插件,从而形成它自己的生态系统。

    主要关系

    • reducer 声明了state的初始值,以及当前state接受action对象之后处理为new state的逻辑。
    • createStore接受reducer作为参数,返回产生的store,store其实是一个含有state的闭包,以及提供将action分发给reducer的dispatch方法。
    • applyMiddlewares方法接受n个middlewares作为参数返回一个用于渲染creatorStore函数的方法。
    • applyMiddleware可以向actionCreator提供store.dispatch以及getState方法,用以增强actionCreator的能力

    store主要包含以下三个核心方法:

    • subscribe 注册store更新之后的回调函数
    • getState 获取store当前state的引用,切记直接修改返回的结果
    • dispatch 将action按顺序经过各middle处理后派发给reducer

    action 流程图

    流程图流程图

    createStore

    createStore是根据reducer中的规则创建store的方法。

    特性

    1. 提供dispatch
    2. subscribe
    3. getState // getState拿到的是state的引用!不要直接修改
    4. 提供初始值initialState

    源码

    //此处为示意,不是 redux 的源码本身
    export default createStore(reducer, initialState) {
        //闭包私有变量
        let currentState = initialState
        let currentReducer = reducer
        let listeners = []
    
        //返回一个包含可访问闭包变量的公有方法
        return {
            getState() {
                return currentState //返回当前 state
            },
            subscribe(listener) {
                let index = listeners.length
                listeners.push(listener) //缓存 listener
                return () => listeners.splice(i, 1) //返回删除该 listener 的函数
            },
            dispatch(action) {
                //更新 currentState
                currentState = currentReducer(currentState, action)
                // 可以看到这里并没有用到eventEmitter等
                listeners.slice().forEach(listener => listener())
                return action //返回 action 对象
            }
        }
    }
    

    action

    action有以下特点:

    • pure object
    • 描述reducer响应的事件类型
    • 携带所需要的数据

    actionCreator

    用于描述action的dispatch的逻辑。

    • action的重用
    • 数据的预处理
    • action的特殊处理逻辑

    reducer

    reducer应该是是一个无副作用函数,以当前的state以及action为参数,返回新的state。
    每次返回一个新State的好处是在shouldComponentUpdate过程中可以使用高性能的shallow equal。

    1. pure function
    2. 接受initialState
    3. don't modify the state!!!
    //reducer 接受两个参数,全局数据对象 state 以及 action 函数返回的 action 对象
    //返回新的全局数据对象 new state
    export default (state, action) => {
        switch (action.type) {
            case A:
            return handleA(state)
            case B:
            return handleB(state)
            case C:
            return handleC(state)
            default:
            return state //如果没有匹配上就直接返回原 state
        }
    }
    

    combineReducers

    将一个reducer map转换为一个reducer。方便对复杂的reducer进行功能拆分。

    problem

    1. state 结构太复杂
    2. 希望根据对应的component进行维护

    how to use

    var reducers = {
        todos: (state, action) { //预期此处的 state 参数是全局 state.todos 属性
            switch (action.type) {...} //返回的 new state 更新到全局 state.todos 属性中
        },
        activeFilter: (state, action) { //预期拿到 state.activeFilter 作为此处的 state
            switch (action.type) {...} //new state 更新到全局 state.activeFilter 属性中
        }
    }
    
    //返回一个 rootReducer 函数
    //在内部将 reducers.todos 函数的返回值,挂到 state.todos 中
    //在内部将 reducers.activeFilter 函数的返回值,挂到 state.activeFilter 中
    var rootReducer = combineReducers(reducers)
    

    源码

    //combination 函数是 combineReducers(reducers) 的返回值,它是真正的 rootReducer
    //finalReducers 是 combineReducers(reducers) 的 reducers 对象去掉非函数属性的产物
     //mapValue 把 finalReducers 对象里的函数,映射到相同 key 值的新对象中
    function combination(state = defaultState, action) {
        var finalState = mapValues(finalReducers, (reducer, key) => {
          var newState = reducer(state[key], action); //这里调用子 reducer
          if (typeof newState === 'undefined') {
            throw new Error(getErrorMessage(key, action));
          }
          return newState; //返回新的子 state
        });
        //...省略一些业务无关的代码
        return finalState; //返回新 state
     };
    
    function mapValues(obj, fn) {
      return Object.keys(obj).reduce((result, key) => {
        result[key] = fn(obj[key], key);
        return result;
      }, {});
    }
    
    

    applyMiddleWares

    problem

    • 异步action
    • promise
    • 个性化 action 响应
    • log

    描述

    接受 middleWares 将 store 修饰为使用了 middlwares 的 store,其实是用被高阶函数修饰过的dispatch替换掉了原来的dispatch。

    usage

    var craeteStoreWithMiddleWare = applyMiddleWare(thunk)(createStore);
    //redux-thunk
    export default function thunkMiddleware({ dispatch, getState }) {
      return next => action =>
        typeof action === 'function' ? // action 居然是函数而不是 plain object?
          action(dispatch, getState) : //在中间件里消化掉,让该函数控制 dispatch 时机
          next(action); //否则调用 next 让其他中间件处理其他类型的 action
    }
    

    源码

    这里的composeMiddleware可能不是很好理解,这里有一个简单的例子方便大家理解。http://jsbin.com/xalunadofa/1/edit?js,console。 compose可以理解为倒叙一层层打包的过程,因此最后调用composedFunction的时候会顺序进入各个middlewares。

    function applyMiddleware(...middlewares) {
      return next => (...args) => {
        const store = next(...args);
        const middleware = composeMiddleware(...middlewares);
    
          // dispatch 被middlWare修饰
        function dispatch(action) {
          const methods = {
            dispatch,
            getState: store.getState
          };
    
          return compose(
            middleware(methods),
            store.dispatch
          )(action);
        }
    
          // 返回新的store dispatch被新的dispatch替代
        return {
          ...store,
          dispatch
        };
      };
    }
    
    

    bindActionCreator

    源码

    //将 actionCreator 跟 dispatch 绑定在一起
    let bindActionCreator => (actionCreator, dispatch) {
      return (...args) => dispatch(actionCreator(...args));
    }
    
    function bindActionCreators(actionCreators, dispatch) {
      if (typeof actionCreators === 'function') { //如果是单个 actionCreator,绑定一词
        return bindActionCreator(actionCreators, dispatch);
      }
      //返回一个改造过的「函数组合」
      return mapValues(actionCreators, actionCreator =>
        bindActionCreator(actionCreator, dispatch)
      )
    }
    

    connector

    connector 接受mapStateToProps, mapDispatchToProps, Component 三个参数,返回一个能够自动关联store中state以及dispatch事件的smart component

    由于connector代码过长,只对重要的几个函数进行说明。
    connetor函数接受的两个参数指明从store的state中挑选哪些作为props,以及将哪些actionCreator绑定到porps中。
    订阅store的change事件,当store更新时计算新的state,与旧state进行浅对比,如果不同则更新state,并render,否则不进行render。

        // 根据从store中select的state以及dispatch绑定的actionCreator计算新的props
       computeNextState(props = this.props) {
         return computeNextState(
           this.stateProps,
           this.dispatchProps,
           props
         );
       }
    
        // 与旧值进行shallow equal
       updateState(props = this.props) {
         const nextState = this.computeNextState(props);
         if (!shallowEqual(nextState, this.state.props)) {
           this.setState({
             props: nextState
           });
         }
       }
    
        // 订阅change事件
       trySubscribe() {
         if (shouldSubscribe && !this.unsubscribe) {
           this.unsubscribe = this.store.subscribe(::this.handleChange);
           this.handleChange();
         }
       }
    
       tryUnsubscribe() {
         if (this.unsubscribe) {
           this.unsubscribe();
           this.unsubscribe = null;
         }
       }
    
       componentDidMount() {
         this.trySubscribe();
       }
    
    
       componentWillUnmount() {
         this.tryUnsubscribe();
       }
    
       handleChange() {
         if (!this.unsubscribe) {
           return;
         }
    
         if (this.updateStateProps()) {
           this.updateState();
         }
       }
    
    

    结语

    欢迎大家发起pr完善文档,进行讨论。

    参考资料

    相关文章

      网友评论

          本文标题:redux 源码解读

          本文链接:https://www.haomeiwen.com/subject/evbvdttx.html