美文网首页redux
redux源码阅读

redux源码阅读

作者: 漂泊的小蘑菇 | 来源:发表于2019-03-10 18:07 被阅读0次

    Redux 是可预测的状态管理框架,它很好的解决多交互,多数据源的诉求。

    三大原则:

    单一数据源:
    整个应用的state被存储在一颗object tree中,并且这颗 object tree存在于唯一的store里,store就是存储数据的容器,容器里面会维护整个应用的state。store提供四个API:dispatch subscribe getState replaceReducer。 数据源是唯一的,所以获取数据的唯一方式就是store.getState()。
    state只读:
    根据state只读原则,state的变更要通过store的dispatch(action)方法,action就是变更数据的载体,action = {type: '', payload},type是变更数据的唯一标志, payload是需要变更的数据。
    使用纯函数变更state
    reducer是纯函数,接受两个参数即 state和action, 根据action的type属性执行对应的代码,更改相应的数据,然后返回新的state。

    完整的数据流过程:

    view层触发actionCreator, actionCreator通过store.dispatch(action) ,reducer执行对应的代码,返回新的state,更新页面。


    image.png

    源码结构:

    src
    ├── utils ---------------------------------------- 工具函数
    ├── applyMiddleware.js --------------------------- 加载 middleware
    ├── bindActionCreators.js ------------------------ 生成将 action creator 包裹在 dispatch 里的函数
    ├── combineReducers.js --------------------------- 合并 reducer 函数
    ├── compose.js ----------------------------------- 组合函数
    ├── createStore.js ------------------------------- 创建一个 Redux store 来储存应用中所有的 state
    ├── index.js ------------------------------------- 入口 js
    

    index.js

    index.js文件是整个代码的入口,该文件暴露了一些接口供开发者使用: createStore, combineReducers, bindActionCreators, applyMiddleware, compose,接下来逐个分析这些接口。

    createStore

    createStore是redux最重要的API,它创建一个store,保存应用中的state。
    createStore函数接受三个参数:reducer, preloadedState, enhancer,
    返回值:dispatch、subscribe、getState、replaceReducer 和 [$$observable],这就是我们开发中主要使用的几个接口。

    参数

    reducer: 函数,返回下一个状态,接收state和action作为参数
    preloadedState: state初始值,可以忽略不传
    enhancer:store的增强器,一般是第三方中间件,返回一个增强后的store creator,这个函数通常由applyMiddleware函数来生成。

    返回值

    dispatch

    function dispatch(action) {
        if (!isPlainObject(action)) {
          throw new Error(
            'Actions must be plain objects. ' +
              'Use custom middleware for async actions.'
          )
        }
        // 判断 action 是否有 type{必须} 属性
        if (typeof action.type === 'undefined') {
          throw new Error(
            'Actions may not have an undefined "type" property. ' +
              'Have you misspelled a constant?'
          )
        }
        // 如果正在 dispatch 则抛出错误
        if (isDispatching) {
          throw new Error('Reducers may not dispatch actions.')
        }
        // 对抛出 error 的兼容,但是无论如何都会继续执行 isDispatching = false 的操作
        try {
          isDispatching = true
          // 使用 currentReducer 来操作传入 当前状态和 action,返回处理后的状态
          currentState = currentReducer(currentState, action)
        } finally {
          isDispatching = false
        }
    
        const listeners = (currentListeners = nextListeners)
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i]
          listener()
        }
    
        return action
      }
    

    dispatch函数是触发动作的函数,接收action行为作为参数,action必须是一个对象,且该对象必须有type参数,如果action满足条件,则调用currentReducer(其实就是createStore的参数 reducer的引用),currentReducer会根据action来改变对应的值,生成新的store,然后再遍历nextListeners列表,调用每一个监听函数。

    getState

    // 读取由 store 管理的状态树
    function getState() {
      if (isDispatching) {
        throw new Error(
          'You may not call store.getState() while the reducer is executing. ' +
            'The reducer has already received the state as an argument. ' +
            'Pass it down from the top reducer instead of reading it from the store.'
        )
      }
    
      return currentState
    }
    

    这个函数可以获取当前的状态,createStore 中的 currentState 储存当前的状态树,这是一个闭包,这个参数会持久存在,并且所有的操作状态都是改变这个引用,getState 函数返回当前的 currentState。

    subscribe

    function subscribe(listener) {
      // 判断传入的参数是否为函数
      if (typeof listener !== 'function') {
        throw new Error('Expected the listener to be a function.')
      }
    
      if (isDispatching) {
        throw new Error(
          'You may not call store.subscribe() while the reducer is executing. ' +
            'If you would like to be notified after the store has been updated, subscribe from a ' +
            'component and invoke store.getState() in the callback to access the latest state. ' +
            'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
        )
      }
    
      let isSubscribed = true
    
      ensureCanMutateNextListeners()
      nextListeners.push(listener)
    
      return function unsubscribe() {
        if (!isSubscribed) {
          return
        }
    
        if (isDispatching) {
          throw new Error(
            'You may not unsubscribe from a store listener while the reducer is executing. ' +
              'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
          )
        }
    
        isSubscribed = false
    
        ensureCanMutateNextListeners()
        const index = nextListeners.indexOf(listener)
        nextListeners.splice(index, 1)
      }
    }
    

    除去一些边界条件的判断,subscribe函数最主要的是给store状态添加监听函数,该函数接收一个函数作为参数,会往nextListeners监听列表加入这个函数,然后会返回一个unsubscribe函数,用于解绑,如果解绑,就从nextListeners列表中去掉该函数,一旦调用dispatch改变store,监听函数就会全部执行。

    combineReducers

    reducer是管理state的一个模块,在应用初始化的时候,它返回initialState,当用户调用action时,它会根据action的type属性,进行相应的更新,reducer是纯函数,不会更改传入的state,会返回新的state。
    当所有的reducer逻辑都写在同一个reducer函数里面会非常的庞大,所以我们会将reducer进行适当的拆分,但是最终传入createStore里面的是唯一的reducer函数,所以我们传入createStore前要进行合并,就需要combineReducers方法。

    参数
    reducers (Object): 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。

    返回值
    (Function): 它是真正 createStore 函数的 reducer,接受一个初始化状态和一个 action 参数;每次调用的时候会去遍历 finalReducer(有效的 reducer 列表),然后调用列表中每个 reducer,最终构造一个与 reducers 对象结构相同的 state 对象。

    combineReducers用法

    rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
    // This would produce the following state object
    {
      potato: {
        // ... potatoes, and other state managed by the potatoReducer ...
      },
      tomato: {
        // ... tomatoes, and other state managed by the tomatoReducer, maybe some nice sauce? ...
      }
    }
    

    源码

    export default function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers)
      const finalReducers = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
    
       /* if (process.env.NODE_ENV !== 'production') {
          if (typeof reducers[key] === 'undefined') {
            warning(`No reducer provided for key "${key}"`)
          }
        }*/
    
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      }
      const finalReducerKeys = Object.keys(finalReducers)
    
      /*let unexpectedKeyCache
      if (process.env.NODE_ENV !== 'production') {
        unexpectedKeyCache = {}
      }
    
      let shapeAssertionError
      try {
        assertReducerShape(finalReducers)
      } catch (e) {
        shapeAssertionError = e
      }*/
    
      return function combination(state = {}, action) {
        if (shapeAssertionError) {
          throw shapeAssertionError
        }
    
        if (process.env.NODE_ENV !== 'production') {
          const warningMessage = getUnexpectedStateShapeWarningMessage(
            state,
            finalReducers,
            action,
            unexpectedKeyCache
          )
          if (warningMessage) {
            warning(warningMessage)
          }
        }
    
        let hasChanged = false
        const nextState = {}
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]
          const reducer = finalReducers[key]
          const previousStateForKey = state[key]
          const nextStateForKey = reducer(previousStateForKey, action)
          if (typeof nextStateForKey === 'undefined') {
            const errorMessage = getUndefinedStateErrorMessage(key, action)
            throw new Error(errorMessage)
          }
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        return hasChanged ? nextState : state
      }
    }
    

    combineReducer函数返回一个combination 函数,该函数接收state和action作为参数,每次调用该函数时:

    1、 for (let i = 0; i < finalReducerKeys.length; i++) { ... }:遍历 finalReducer(有效的 reducer 列表);
    2、 var previousStateForKey = state[key]:当前遍历项的之前状态,看到这里就应该明白传入的 reducers 组合为什么 key 要和 store 里面的 state 的 key 相对应了;
    3、 var nextStateForKey = reducer(previousStateForKey, action):当前遍历项的下一个状态;
    4、 nextState[key] = nextStateForKey:将 当前遍历项的下一个状态添加到 nextState;
    5、 hasChanged = hasChanged || nextStateForKey !== previousStateForKey:判断状态是否改变;
    6、 return hasChanged ? nextState : state:如果没有改变就返回原有状态,如果改变了就返回新生成的状态对象。

    applyMiddleware

    bindActionCreators

    1. actionCreator创建动作
      在分析之前先明确ActionCreator是什么?ActionCreator是动作创造者或者说是动作工厂,如果我们想根据不同的参数来生成不同值的计数器,例子如下:
      const counterActionCreator = (step) => {
        return {
          type: 'increment',
          step:  step || 1,
        }
      }
    

    2.bindActionCreator
    从上述例子出发,如果我们想生成不同的计数器,并分发他们,则需要按照以下写法:

    const action1 = counterActionCreator();
    dispatch(action1);
    
    const action2 = counterActionCreator(2);
    dispatch(action2);
    
    const action3 = counterActionCreator(3);
    dispatch(action3);
    

    从上面的写法来看,每次都需要去调用actionCreator,再调用dispatch分发action,会产生很多繁杂重复的代码,所以我们可以采用 bindActionCreators.js 文件里面的bindActionCreator 方法来优化代码,

    bindActionCreator 源码:
    function bindActionCreator(actionCreator, dispatch) {
      return function() {
        return dispatch(actionCreator.apply(this, arguments))
      }
    }
    

    bindActionCreator会返回一个函数,函数中会调用actionCreator生成action,并将action作为dispatch的参数分发掉,所以我们将action的生成动作和调用动作封装到了一个函数里面,我们直接调用bindActionCreator返回的函数就行,不用再每次去调用actionCreator,再调用dispatch分发action,例子如下:

    const increment = bindActionCreator(counterActionCreator, dispatch);
    
    increment();
    increment(2);
    increment(3);
    
    1. bindActionCreators
      接下来看bindActionCreators函数,其实是对bindActionCreator的增强,去掉一些判断条件,源码部分具体如下:
    function bindActionCreators(actionCreators, dispatch) {
      if (typeof actionCreators === 'function') { // #1
        return bindActionCreator(actionCreators, dispatch) // #2
      }
    
      ....
    
      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') { // #3
          boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
        }
      }
      return boundActionCreators
    }
    

    当actionCreators是一个函数时,直接调用bindActionCreator将整个过程封装返回即可
    当actionCreators是一个有多个函数方法组成的对象时,遍历该对象,如果键对应的值是函数,则调用bindActionCreator将整个过程封装,并将封装结果赋值给该键,最后返回一个对象,对象里面的值都被bindActionCreator封装过。
    actionCreators为对象时的例子如下:

    const actionCreators = {
      increment: function(step) {
        return {
          type: 'INCREMENT',
          step: step || 1
        }
      },
    
      decrement: function(step) {
        return {
          type: 'DECREMENT',
          step: - (step || 1)
        }
      }
    }
    
    const newActionCreators = bindActionCreators(MyActionCreators, dispatch)
    
    newActionCreators.increment();
    newActionCreators.increment(1);
    
    newActionCreators.decrement();
    newActionCreators.decrement(1);
    

    相关文章

      网友评论

        本文标题:redux源码阅读

        本文链接:https://www.haomeiwen.com/subject/djibeqtx.html