Redux 源碼學習

想着對 React 生態更深刻的學習,何況 React 源碼內容也很少的狀況,來讀一下他的源碼內容。 目前 Redux 已是 TypeScript 版本的源碼,我正在閱讀的則是 4.0.4 版本代碼。typescript

很顯然,能夠看到這和 React 倉庫同樣,一樣使用了 rollup 打包工具,也驗證了 rollup 在開發庫類倉庫的優點,畢竟大廠都在用嘛。json

咱們先來看到 package.jsonredux

{
  "name": "redux",
  "version": "4.0.4",
  "main": "lib/redux.js",
  "unpkg": "dist/redux.js",
  "module": "es/redux.js",
  "types": "types/index.d.ts",
}
複製代碼

我省略了大部份內容,能夠看到開發團隊針對不一樣使用場景,對外提供包對應的路徑,好比使用 TypeScript 時,對應的則是 types/index.d.ts ,或者使用 ES6 模塊化引入時則是 es/redux.js 。值得咱們學習。api

接下來,咱們看到源碼部分,整個源碼學習內容,將圍繞着 src 進行, utils 下的內容即一些簡單的工具, types 下則是一些類型的定義。數組

示例代碼中我會移除並省略掉一些邏輯代碼和 TypeScript 相關的函數重載代碼,以及一些我認爲不那麼重要的代碼,即只展示我想要說明的內容。markdown

那麼咱們先來看到 createStore 函數吧。app

createStore

export default function createStore< S, A extends Action, Ext = {},
  StateExt = never
>(
  reducer: Reducer<S, A>,
  preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
  enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
  // 接受的 reducer
  let currentReducer = reducer
  // 預加載 state
  let currentState = preloadedState as S
  // 訂閱隊列
  let currentListeners: (() => void)[] | null = [] // 下一個訂閱隊列,看起來更像是訂閱隊列的副本,下面我會來介紹他和訂閱隊列的不解之謎 let nextListeners = currentListeners // 檢查並建立這個副本的函數 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { // 拷貝到一個新的數組引用,能夠理解爲 [...currentListeners] 使得 nextListeners !== currentListeners nextListeners = currentListeners.slice() } } // 得到 state function getState(): S { return currentState as S } // 訂閱函數 function subscribe(listener: () => void) { ensureCanMutateNextListeners() // 操做訂閱隊列副本,與 currentListeners 之間不受影響 nextListeners.push(listener) // 返回取消訂閱函數 return function unsubscribe() { ensureCanMutateNextListeners() // 一樣的效果 const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } // 看到這裏你是否是會想,建立這個副本的目的是什麼? // 官方給到的解釋:This prevents any bugs around consumers calling subscribe/unsubscribe in the middle of a dispatch. } // dispatch 函數 function dispatch(action: A) { currentState = currentReducer(currentState, action) // 遍歷通知訂閱隊列,將 currentListeners 賦值爲「最新」的隊列 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } // 替換 reducer 函數 function replaceReducer<NewState, NewActions extends A>( nextReducer: Reducer<NewState, NewActions> ): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext { // 更新 reducer ;((currentReducer as unknown) as Reducer< NewState, NewActions >) = nextReducer // 同初始化功能一致,即生成 currentState ,下面代碼中會說起 dispatch({ type: ActionTypes.REPLACE } as A) return (store as unknown) as Store< ExtendState<NewState, StateExt>, NewActions, StateExt, Ext > & Ext } // 初始化,生成 currentState ,即調用 getState 函數返回的內容, ActionTypes.INIT 便可以理解爲私有類型,不會影響到你本身寫的 reducer 畢竟裏面包含了隨機數... dispatch({ type: ActionTypes.INIT } as A) // 暴露的 store 對象 const store = ({ dispatch: dispatch as Dispatch<A>, subscribe, getState, replaceReducer, // 觀察者方法這裏不作解讀,能夠理解爲對 subscribe 的封裝 [?observable]: observable } as unknown) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext return store } 複製代碼

那麼到此,咱們看到了整個 createStore 函數的實現過程以及返回的內容。模塊化

這裏我來簡單的代過 compose 函數,也是業界的經常使用實現,不是很明白其功能的同窗能夠簡單看到這段代碼便可:函數

(funcs as : Function[]).reduce((a, b) => (...args: any) => a(b(...args)))
複製代碼

接下來,咱們來看看 combineReducers 的實現,你能夠把這個函數理解爲對 reducer 的合併,固然函數名就是這個意思。工具

combineReducers

export default function combineReducers(reducers: ReducersMapObject) {
  const reducerKeys = Object.keys(reducers)
  // 用於 combination 函數中使用所需,收集的有效 reducers
  const finalReducers: ReducersMapObject = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  // 收集的 keys
  const finalReducerKeys = Object.keys(finalReducers)

  return function combination( state: StateFromReducersMapObject<typeof reducers> = {}, action: AnyAction ) {
    let hasChanged = false
    // 新的 state
    const nextState: StateFromReducersMapObject<typeof reducers> = {}
    // 遍歷 keys
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      // 從遍歷中更新 state
      const nextStateForKey = reducer(previousStateForKey, action)
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    // 返回 state 或新的 state
    return hasChanged ? nextState : state
  }
}
複製代碼

在咱們看完 reducer 的組合工具函數後,咱們來看到綁定執行上下文的語法糖函數 bindActionCreators

bindActionCreators

實現代碼不多,即綁定執行上下文環境 this 。具體使用場景,能夠看到官網 API 文檔 bindActionCreators(actionCreators, dispatch) ,那麼直接看到源碼:

function bindActionCreator<A extends AnyAction = AnyAction>( actionCreator: ActionCreator<A>, dispatch: Dispatch ) {
  return function(this: any, ...args: any[]) {
    return dispatch(actionCreator.apply(this, args))
  }
}

export default function bindActionCreators( actionCreators: ActionCreator<any> | ActionCreatorsMapObject, dispatch: Dispatch ) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  const boundActionCreators: ActionCreatorsMapObject = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}
複製代碼

最後,咱們來看到中間件函數 applyMiddleware

applyMiddleware

export default function applyMiddleware( ...middlewares: Middleware[] ): StoreEnhancer<any> {
  return (createStore: StoreCreator) => <S, A extends AnyAction>(
    reducer: Reducer<S, A>,
    ...args: any[]
  ) => {
    const store = createStore(reducer, ...args)
    let dispatch: Dispatch = () => {
      throw new Error('...')
    }

    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 對 store.dispatch 進行改寫,將其包裝在中間件的最後一層,以實現中間件的功能
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
複製代碼

OK, Redux 源碼學習完了,是否是沒看夠,確實!

這纔是精妙的庫設計,簡潔的代碼實現強大的功能,那麼此次沒過癮不要緊,下次咱們來品一品 Redux 是如何和 React 結合在一塊兒的吧。

相關文章
相關標籤/搜索