redux 源碼解讀

前言

redux並不侷限於flux與react。redux 自身保持簡潔以便適配各類場景,讓社區發展出各類 redux-* 中間件或者插件,從而造成它本身的生態系統。html

主要關係

  • reducer 聲明瞭state的初始值,以及當前state接受action對象以後處理爲new state的邏輯。react

  • createStore接受reducer做爲參數,返回產生的store,store實際上是一個含有state的閉包,以及提供將action分發給reducer的dispatch方法。git

  • applyMiddlewares方法接受n個middlewares做爲參數返回一個用於渲染creatorStore函數的方法。github

  • applyMiddleware能夠向actionCreator提供store.dispatch以及getState方法,用以加強actionCreator的能力redux

store主要包含如下三個核心方法:數組

  • subscribe 註冊store更新以後的回調函數promise

  • getState 獲取store當前state的引用,切記直接修改返回的結果緩存

  • dispatch 將action按順序通過各middle處理後派發給reducer閉包

action 流程圖

流程圖

createStore

createStore是根據reducer中的規則建立store的方法。app

特性

  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完善文檔,進行討論。

參考資料

相關文章
相關標籤/搜索