redux 中心源码剖析

redux 中心源码剖析

本文默许,你已经有了肯定的es6基本。源码有删减,对个中的中心逻辑做诠释

  • redux 是 一个用 javascript 治理数据的容器,这个容器的状况是能够展望的。
  • redux 能够跟任何是个 视图(view)层的框架连系应用
  • 开辟react应用,常常连系应用 redux
  • redux 比较笼统,中心旨在定义一个数据读写的范例,和数据变化后的回调接口

三大原则

单一数据源 :全部应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于唯一一个 store 中。

State 是只读的:唯一转变 state 的要领就是触发 action,action 是一个用于形貌已发作事宜的一般对象。

应用纯函数来实行修正 :为了形貌 action 怎样转变 state tree ,你须要编写 reducers。纯函数:http://web.jobbole.com/86136/

createStore.js 中心逻辑

function createStore(reducer, preloadedState, enhancer) {
    /*
      参数花样/范例 婚配。
      reducer:定义 state 怎样更新
      preloadedState: 初始状况
      enhancer 是让中间件依据划定模子(洋葱模子/回形针模子)实行的函数,就是下文将会引见的 applyMiddleware函数的返回值
    */
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
      enhancer = preloadedState
      preloadedState = undefined
    }
    if (typeof enhancer !== 'undefined') {
      if (typeof enhancer !== 'function') {
        throw new Error('Expected the enhancer to be a function.')
      }
      return enhancer(createStore)(reducer, preloadedState)
    }
  
    let currentReducer = reducer
    let currentState = preloadedState
    let currentListeners = []
    let nextListeners = currentListeners
  
    /**
     *  返回 store 中的 state。
     *  State 是只读的:这个 state 是 createStore 函数内部变量,只能经由过程 createStore 供应的 getState 要领读取 state。
     */
    function getState() {
      return currentState
    }
    
    /**
     * listener 是函数,保护在内部变量 nextListeners(数组) 中
     * 挪用 subscribe(定阅) 要领,增加 listener 在数组中
     * 并返回一个要领,能够把当前增加在数组中的 listener 函数掏出。
     * 
     * listener 的作用:当 state 发作变化的时刻,listener详细去做些事变,比方更新 UI。
     * 只定义做事变的机遇,详细做什么由挪用者本身完成
     */
    function subscribe(listener) {
      nextListeners.push(listener)
      return function unsubscribe() {
        const index = nextListeners.indexOf(listener)
        nextListeners.splice(index, 1)
      }
    }
    
    /**
     * dispatch的作用:变动 state
     * 变动的体式格局:依据传入的 reducer 和 action,发生一个新的 state。
     * 
     * action的请求:是一个地道的对象字面量,而且具有 type 字段
     * 经由 reducer 依据 action 把 当前 state 更新。一旦更新完毕,马上把数组中的每一个 listener 挪用一遍
    */
    function dispatch(action) {
      if (!isPlainObject(action)) {
        throw new Error(
          'Actions must be plain objects. ' +
            'Use custom middleware for async actions.'
        )
      }
  
      if (typeof action.type === 'undefined') {
        throw new Error(
          'Actions may not have an undefined "type" property. ' +
            'Have you misspelled a constant?'
        )
      }
  
      currentState = currentReducer(currentState, action)
  
      const listeners = (currentListeners = nextListeners)
      for (let i = 0; i < listeners.length; i++) {
        const listener = listeners[i]
        listener()
      }
  
      return action
    }
  
    /**
     * 能够变动 reducer 
     * reducer 一旦变动,马上 dispatch 一次,把 state 更新,把注册的 listener 实行一遍
     */
    function replaceReducer(nextReducer) {
      if (typeof nextReducer !== 'function') {
        throw new Error('Expected the nextReducer to be a function.')
      }
  
      currentReducer = nextReducer
      dispatch({ type: ActionTypes.REPLACE })
    }
  
    /**
     * 一旦 挪用 createStore 函数,马上 dispatch 一遍,把 state 更新,把注册的 listener 实行一遍
     */
    dispatch({ type: ActionTypes.INIT })
  
    return {
      dispatch,
      subscribe,
      getState,
      replaceReducer,
    }
  }

redux 中的中间件

redux 中的中间件一个典范的应用场景是处置惩罚异步接口返回数据

罕见的 web 体系中的中间件,不侵入营业逻辑,功用优盘化,即用即插。

《redux 中心源码剖析》

洋葱模子/回形针模子
    middleWareA(middleWareB(middleWareC('hello world')));
    /**
     * 挪用后的结果: 
     * hello world form middleWareC;form middleWareB;form middleWareA;
     * */ 

    function middleWareA(string){
        return `${string} form middleWareA;`;
    };
    function middleWareB(string){
        return `${string} form middleWareB;`;
    };
    function middleWareC(string){
        return `${string} form middleWareC;`;
    };

对上面的代码换一种展示体式格局

    middleWareA(
        middleWareB(
            middleWareC('hello world')
        )
    );

中间件 middleWareA 要实行,先得实行 middleWareB。中间件 middleWareB 要实行,先得实行 middleWareC,数据层层通报
挪用递次为: A–>B–>C
实行完毕的递次为:C–>B–>A
这就是 洋葱模子 或许 回形针模子
下面看一个 redux-thunk 中间件

redux-thunk 中心逻辑
function createThunkMiddleware() {
  /**
   * dispatch 是基于 store.dispatch,且层层经由中间件包装后的要领
   * getState 是 createStore 函数实行后导出的要领;
   * next 为下一个中间件
   * action 的预期花样为 createStore 中设想的 action的花样,是一个字面量对象。在这里做action范例的推断
  */
  return ({ dispatch, getState }) => next => action => {
    // 假如 action 是函数
    if (typeof action === 'function') {
        return action(dispatch, getState);
    }
    // 假如不是函数,流转到下一个中间件,期待末了的一个中间件的 action 是一个预期形式的 action
    return next(action);
  };
}

export default createThunkMiddleware();

applyMiddleware 中心逻辑

/**
 * middlewares 是包括中间件的数组
 * 须要的中间件,顺次传入。如: applyMiddleware(middlewaresA, middlewaresB, middlewaresC)
 * applyMiddleware 实行后的返回的函数就是一个 enhancer
*/
export default function applyMiddleware(...middlewares) {
    return createStore => (...args) => {
      // args解构后,就是 reducer, preloadedState
      // 这里的 store 就是 createStore 函数临盆的全局的状况树,拜见上文对 createStore 的诠释
      const store = createStore(...args)
  
      let dispatch = () => { };
      let chain = []
  
      /**
       * 每一个中间件中须要通报的数据
       */
      const middlewareAPI = {
        getState: store.getState,// 猎取 state 的函数,用于读
        dispatch: (...args) => dispatch(...args)// 更新 state 的函数,用于写。
      }
      // 遍历中间件
      chain = middlewares.map(middleware => middleware(middlewareAPI))
      
      // 下面的代码的结果,就是要应用洋葱模子/回形针模子:middlewareA(middlewareB(middlewareC(store.dispatch)))
      // 关于 compose 比较简单:https://github.com/reactjs/redux/blob/master/src/compose.js
      // 中间件的最先是 原生的store.dispatch,完毕时是 一个携带了种种中间件信息的 dispatch
      dispatch = compose(...chain)(store.dispatch)
  
      /**
       * 特别注重这里的 dispatch
       * 最先时定义了一个空的 dispatch 函数,是为了给 middlewareAPI 的 dispatch 一个初始值
       * 实际上真正的 dispatch 是中间件依据原生的 store.dispatch 依据洋葱模子/回形针模子,实行了一边,挪用一切中间件后的 dispatch
       * 这个 dispatch 携带了传入的每一个中间件的操纵。一次假如此时传入 action 更新 state,一切的中间件都邑被实行一遍。
      */
  
      return {
        ...store,
        dispatch
      }
    }
  }
    原文作者:xiezhongfu
    原文地址: https://segmentfault.com/a/1190000012925538
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞