redux 源碼解讀

前言

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

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容