Redux-原始碼解析

mazy發表於2018-06-26

image

整個redux的原始碼非常簡單,所以解釋很少,看程式碼就能看明白......

combineReducers


為啥先說這個

前提先看middleware: Redux-Middleware-原始碼解析, 這些都是解析createStore的前提


//傳入的reducers是個物件
//將多個reducer捆成一個
export default function combineReducers(reducers) {
  //取出所有reducer的key=> {key1:value,key2:value}
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    //取出key
    const key = reducerKeys[i]

    if (typeof reducers[key] === 'function') {
      //可以代表的value是函式的話,儲存進 finalReducers 物件中
      finalReducers[key] = reducers[key]
    }
  }

  //取出所有value是函式的key
  const finalReducerKeys = Object.keys(finalReducers)

  let shapeAssertionError
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }
  //返回一個 combination 函式,方法接收state,action引數 這個返回的函式是createStore的第一個引數
  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    let hasChanged = false
    const nextState = {}
    //迴圈每一個reducer,
    for (let i = 0; i < finalReducerKeys.length; i++) {
      //取出key
      const key = finalReducerKeys[i]
      //取出key對應的reducer
      const reducer = finalReducers[key]
      //根據key在state獲取狀態
      //state的狀態都是根據reducer的名字進行儲存的
      const previousStateForKey = state[key]
      //將state和action傳入reducer中
      const nextStateForKey = reducer(previousStateForKey, action)
      //將nextStateForKey reducer 放入到nextState中
      nextState[key] = nextStateForKey
      //在state中的狀態和reducer中返回的狀態是否一樣
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    //返回新的state
    return hasChanged ? nextState : state
  }
}

複製程式碼

是什麼

  1. 名字的含義就是組合reducer
  2. 因為reducers是作為createStore函式的第一個引數,而不是前多少個引數

幹了什麼

export default combineReducers({
    userinfo,
    stores,
    home,
    likeList
})
複製程式碼
  1. 其中的每一個引數都是一個reducer,而且每一個引數的名字都作為在store中的key
  2. 將這些reducer組合成一個reducers,提供給createStore作為引數

流程啥樣

  1. 雖然傳了很多reducer但是先過濾掉不是function的reducer(不知道不是function的reducer是啥)
  2. 將過濾的key存放到finalReducerKeys中,將過濾的reducer存放到finalReducers中,閉包要用
  3. 然後返回一個 combination() 函式,其實這個才是createStore的第一個引數

combination做了什麼

  1. 迴圈每一個有效的reducer
  2. 取出reducer,state中的對應的狀態
  3. 呼叫reducer,並傳入取出的狀態,和action,獲取返回值(新state)
  4. 返回新的state

createStore

//建立store的api,也是redux中最重要的api,而建立的store用於管理應用中的所有state,且只有一個store
export default function createStore(reducer, preloadedState, enhancer) {
  //簡而言之,第二個引數是函式,並且第三個引數是undefined,然後第二第三個引數值互換
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    //這個就是中介軟體,看middleware那篇文章
    return enhancer(createStore)(reducer, preloadedState)
  }

  //儲存當前的reducer
  let currentReducer = reducer
  //儲存傳入的狀態
  let currentState = preloadedState
  //設定當前監聽集合
  let currentListeners = []
  //將當前的監聽集合賦值給下一個監聽集合
  let nextListeners = currentListeners
  let isDispatching = false
  
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      //slice() 沒有引數預設begin為0 就是拷貝下
      nextListeners = currentListeners.slice()
    }
  }

  //獲取當前狀態
  //函式巢狀函式,內部函式引用外部函式的變數,最後返回函式,這是閉包
  function getState() {
    return currentState
  }

  //增加監聽,引數listener是一個回撥函式,在dispatch裡,會呼叫所有的監聽器
  function subscribe(listener) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    //把新增加的監聽加入到當前監聽列表中
    nextListeners.push(listener)
    //返回一個函式,用於去除監聽
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      //解除監聽 isSubscribed 設定為false,意為已經取消監聽
      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      //然後幹掉這個監聽
      nextListeners.splice(index, 1)
    }
  }

  //這個是比較常用的api=>主要用於觸發action,改變狀態
  function dispatch(action) {
    try {
      isDispatching = true//正在dispatch
      //執行reducer 返回新state,呼叫的是combination()方法
      currentState = currentReducer(currentState, action)
    } finally {
      // finally不管報沒報錯最後都要執行
      isDispatching = false
    }
    //執行所有監聽
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }
  //替換reducer 然後更新store
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  //這個是留給內部
  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer) {
        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }
  //初始化store
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
複製程式碼

是什麼

  1. 這個是最核心的api,這個函式是建立store的唯一方式
  2. 並且返回了dispatch,subscribe,getState...比較常用的api

幹了什麼

const store = createStore(rootReducer, applyMiddleware(...middlewares));
複製程式碼
  1. 如果有中介軟體,先處理中介軟體然後回過頭來在執行createStore
  2. 儲存了一些當前的屬性,用於閉包,提供的api

api

getState

這個直接返回了當前的state,因為閉包,所以可以獲取到最新的state

subscribe

負責新增監聽,引數是個回撥函式,將回撥函式加入到監聽集合,並且返回一個取消監聽的函式:這個返回的函式內部封裝了當執行時,在監聽集合中將這個監聽去掉

dispatch

是最核心的api,通過執行reducer(閉包,currentReducer),返回新的state,並賦值給當前的currentState,並執行所有的監聽,返回action

replaceReducer

用於替換reducer,然後呼叫dispatch更新state


bindActionCreators

這個結合connect使用,在mapDispatchToProps中定義,就可以直接用this.props.userInfoActions來呼叫dispatch(action)操作

function mapStateToProps( state ) {
    return {}
}
function mapDispatchToProps( dispatch ) {
    return {
        userInfoActions : bindActionCreators(userInfoActionsFormOtherFile, dispatch)
    }
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(App)
複製程式碼
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  //如果 actionCreators 是一個函式,染回 dispatch(actionCreator.apply(this, arguments))
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  //actionCreators 不是函式,不是物件,不是null
  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  //最後迭代actionCreators裡的元素,如果是函式,就按照函式的方式組成陣列,返回
  return boundActionCreators
}
複製程式碼

是什麼

就跟名字一樣:給actionCreators繫結dispatch,也就是返回一個dispatch(actionCreator.apply(this, arguments))這樣的函式,或者包含多個dispatch的陣列

幹了什麼

  1. 傳入兩個引數,第一個是actionCreator,第二個是dispatch
  2. 如果只是一個actionCreator,那麼直接返回一個包含dispatch(actionCreator.apply(this, arguments))的函式
  3. 如果他是個陣列,就去迴圈這個陣列將每一個actionCreator都繫結dispatch,然後返回這個陣列

總結

看完了原始碼,感覺redux真的沒有太多的東西,真是對映了那句話:用盡量少的程式碼,做盡量多的事情!

combineReducers

  1. 引數reducers是一個多個reducer組合的物件
  2. 去除所有的reducer的key=> reducerKeys
  3. 迴圈reducerKeys通過key取出reducer
  4. 如果reducer是個函式,就加進 finalReducers 中
  5. 取出finalReducers中的所有key=>finalReducerKeys
  6. 然後返回combination,這個是個內部函式,裡面用到了上面的finalReducers和finalReducerKeys
  7. combination中取出每個reducer,根據finalReducerKeys中對應的key,在state中取出響應的狀態
  8. 執行reducer並將引數傳入返回新狀態
  9. 將返回的狀態新增進nextState物件中
  10. 返回新狀態

通過程式碼的執行總結combineReducers

  1. 將多個reducer的物件reducers傳入combineReducers中
  2. 返回 combination函式
  3. 返回的函式作為createStore的第一個引數
  4. 進入createStore中,提供的dispatch()中用到了reducers,並傳入了state和action
  5. 然後執行combination函式,迴圈finalReducers
  6. 執行每一個reducer,通過返回的state,並新增進nextState物件中
  7. 返回的state和引數的state比較,如果兩個state相等,則說明這個key所對應的state沒有變化
  8. 如果不相等說明有變化,標記有變化
  9. 最後根據state是否有變化決定返回 nextState 還是引數 state

相關文章