Redux is a predictable state container for JavaScript apps.

redux 三大原则

单一数据源 Single source of truth:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

state 只读 State is read-only:惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。

使用纯函数来执行修改 Changes are made with pure functions:为了描述 action 如何改变 state tree ,你需要编写 reducers。Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。

redux 一些概念

Action: 是把数据传到 store 的唯一手段,表达修改 state 的意图。它是 store 数据的唯一来源。一般通过 store.dispatch() 将 action 传到 store。


    {
        type:types.RECEIVE_CASES,
        cases
    }

Action Creator:是一个创建 action 的纯函数。


    export function receiveCases(cases) {
      return {
        type:types.RECEIVE_CASES,
        cases
      }
    }

Reducer:是一个纯函数,接收旧的 state 和 action,通常包含了switch结构,根据dispatch传来的state和type来生成新的state。

Store:构建顺序:

reducers => 'combineReducers'
                    => rootReducer => 'createStore' (+ 'applyMiddleware')
                                                    |
                                                   ===> **Store**

createStore.js

  • redux.createStore(reducer, initialState) 传入了reducer、initialState,并返回一个store对象。

  • store对象对外暴露了dispatch、getState、subscribe方法

  • store对象通过getState() 获取内部状态

  • initialState为 store 的初始状态,如果不传则为undefined

  • store对象通过reducer来修改内部状态

  • store对象创建的时候,内部会主动调用dispatch({ type: ActionTypes.INIT });来对内部状态进行初始化。通过断点或者日志打印就可以看到,store对象创建的同时,reducer就会被调用进行初始化。


    /**
     * Creates a Redux store that holds the state tree.
     * The only way to change the data in the store is to call `dispatch()` on it.
     *
     * There should only be a single store in your app. To specify how different
     * parts of the state tree respond to actions, you may combine several reducers
     * into a single reducer function by using `combineReducers`.
     *
     * @param {Function} reducer A function that returns the next state tree, given
     * the current state tree and the action to handle.
     * --- reducer 是 Function,用于构建 state 树
     *
     * @param {any} [preloadedState] The initial state. You may optionally specify it
     * to hydrate the state from the server in universal apps, or to restore a
     * previously serialized user session.
     * If you use `combineReducers` to produce the root reducer function, this must be
     * an object with the same shape as `combineReducers` keys.
     * --- preloadedState 初始化 state 树
     *
     * @param {Function} enhancer The store enhancer. You may optionally specify it
     * to enhance the store with third-party capabilities such as middleware,
     * time travel, persistence, etc. The only store enhancer that ships with Redux
     * is `applyMiddleware()`.
     * --- enhancer 相当于 AOP 插件
     *
     * @returns {Store} A Redux store that lets you read the state, dispatch actions
     * and subscribe to changes.
     * --- 最终返回 Store,能获取 state、分发action、订阅变化
     */
    export default function createStore(reducer, preloadedState, enhancer) {
      ... // --- 校验参数,及执行 enhancer
    
      var currentReducer = reducer
      var currentState = preloadedState // --- state 树
      var currentListeners = [] // --- 注册的监听器列表,实时处理 dispatch 事件
      var nextListeners = currentListeners // --- 注册的监听器列表,实时接收 subscribe 事件
      var isDispatching = false // --- 如果 reducer 正在执行,会抛出异常
    
      // --- 将 nextListeners 做为 currentListeners 的副本
      function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
          nextListeners = currentListeners.slice()
        }
      }
    
      // --- 返回 state
      function getState() {
        ... // --- 校验
    
        return currentState
      }
    
      // --- 注册监听器
      // 很常见的监听函数添加方式,当store.dispatch 的时候被调用
      // store.subscribe(listener) 返回一个方法(unscribe),可以用来取消监听
      function subscribe(listener) {
        listeners.push(listener);
        var isSubscribed = true;
    
        return function unsubscribe() {
          if (!isSubscribed) {
            return;
          }
    
          isSubscribed = false;
          var index = listeners.indexOf(listener);
          listeners.splice(index, 1);
        };
      }
      
      // --- 分发 action,修改 state 的唯一方式
      function dispatch(action) {
        
        // 以下情况会报错
        // 1. 传入的action不是一个对象
        // 2. 传入的action是个对象,但是action.type 是undefined
        try {
          isDispatching = true
          // 就是这一句啦, 将 currentState 设置为 reducer(currentState, action) 返回的值
          currentState = currentReducer(currentState, action) // 执行 reducer,state 被更新
        } finally {
          isDispatching = false
        }
    
        // --- 如果有监听函数,就顺序调用监听器方法
        //listeners.slice().forEach(listener => listener());
        var listeners = currentListeners = nextListeners
        for (var i = 0; i < listeners.length; i++) {
          listeners[i]()
        }
    
        // --- 返回结果还是 action
        return action
      }
    
      // --- 替换 reducer,用于热替换、按需加载等场景
      function replaceReducer(nextReducer) {
        ... // --- 校验
    
        currentReducer = nextReducer
        dispatch({ type: ActionTypes.INIT })
      }
    
      function observable() {
        // --- 略
      }
    
      // --- 创建时初始化应用状态
      // redux.createStore(reducer, initialState) 的时候, 内部会 自己调用 dispatch({ type: ActionTypes.INIT });
      // 来完成state的初始化
      dispatch({ type: ActionTypes.INIT })
    
      return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
      }
    }

combineReducers.js


    function TodoReducer(state, action) {}
    function FilterReducer(state, action) {}
    
    var finalReducers = redux.combineReducers({
        todos: TodoReducer,
        filter: FilterReducer
    });

redux.combineReducers(reducerMap) 用来合成多个reducer,其实就是把所有输入的reducer闭包封装,返回一个combination函数,这个函数会接受state和action,依次调用封装的reducer,分发state和action,来生成新的state

  • combineReducers(reducerMap) 传入一个对象,并返回一个全新的reducer。调用方式跟跟普通的reducer一样,也是传入state、action。

  • 通过combineReducers,对 store 的状态state进行拆分,

  • reducerMap的key,就是 state 的key,而 调用对应的reducer返回的值,则是这个key对应的值。如上面的例子,state.todos == TodoReducer(state, action)

  • redux.createStore(finalReducers, initialState) 调用时,同样会对 state 进行初始化。这个初始化跟通过普通的reducer进行初始化没多大区别。举例来说,如果 initialState.todos = undefined,那么 TodoReducer(state, action) 初始传入的state就是undefined;如果initialState.todos = [],那么 TodoReducer(state, action) 初始传入的state就是[];

  • store.dispatch(action),finalReducers 里面,会遍历整个reducerMap,依次调用每个reducer,并将每个reducer返回的子state赋给state对应的key。


    /**
     * Turns an object whose values are different reducer functions, into a single
     * reducer function. It will call every child reducer, and gather their results
     * into a single state object, whose keys correspond to the keys of the passed
     * reducer functions.
     *
     * @param {Object} reducers An object whose values correspond to different
     * reducer functions that need to be combined into one. One handy way to obtain
     * it is to use ES6 `import * as reducers` syntax. The reducers may never return
     * undefined for any action. Instead, they should return their initial state
     * if the state passed to them was undefined, and the current state for any
     * unrecognized action.
     *
     * @returns {Function} A reducer function that invokes every reducer inside the
     * passed object, and builds a state object with the same shape.
     */
    export default function combineReducers(reducers) {
      // --- 过滤 reducer
      var reducerKeys = Object.keys(reducers)
      var finalReducers = {}
      for (var i = 0; i < reducerKeys.length; i++) {
        var key = reducerKeys[i]
    
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      }
      var finalReducerKeys = Object.keys(finalReducers)
    
      // --- 返回 combination 函数
      return function combination(state = {}, action) {
        // --- 校验
    
        // --- 根据 reducer key 及执行结果构造 state 树
        var hasChanged = false
        var nextState = {}
        for (var i = 0; i < finalReducerKeys.length; i++) {
          var key = finalReducerKeys[i]
          var reducer = finalReducers[key]
          var previousStateForKey = state[key]
          var nextStateForKey = reducer(previousStateForKey, action) // --- 执行 reducer
          // --- 校验
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        return hasChanged ? nextState : state
      }
    }

applyMiddleware.js

applyMiddleware 传入 middleware 链,并返回应用这些 middleware 的 store enhancer


    export default function applyMiddleware(...middlewares) {
      return (next) => (reducer, initialState) => {
        // 内部先创建一个store (相当于直接调用 Redux.createStore(reducer, initialState))
        var store = next(reducer, initialState);
        // 保存最初始的store.dispatch
        var dispatch = store.dispatch;
        var chain = [];
    
        var middlewareAPI = {
          getState: store.getState,
          // 最后面, dispatch 被覆盖, 变成包装后的 dispatch 方法
          dispatch: (action) => dispatch(action)
        };
        // 返回一个数组
        // 贴个例子在这里做参考,redux-thunk
        // function thunkMiddleware(store) {
        //  var dispatch = store.dispatch;
        //  var getState = store.getState;
        //
        //  这里的next其实就是dispatch
        //  return function (next) {
        //    return function (action) {
        //      return typeof action === 'function' ? action(dispatch, getState) : next(action);
        //    };
        //  };
        //}
        /*
          chain 是个数组, 参考上面的 middlleware (redux-thunk),可以看到,chain的每个元素为如下形式的function
          并且, 传入的 store.getState 为原始的 store.getState,而 dispatch则是包装后的 dispatch(不是原始的store.dispatch)
          似乎是为了确保, 在每个middleware里调用 dispatch(action), 最终都是 用原始的 store.dispatch(action)
          避免 store.dispatch 被覆盖, 导致middleware 顺序调用的过程中, store.dispatch的值变化 --> store.dispatch 返回的值可能会有不同
          违背 redux 的设计理念
    
          这里的 next 则为 原始的 store.dispatch (见下面 compose(...chain)(store.dispatch) )
          function (next) {
            return function (action) {
    
            }
          }
         */
        chain = middlewares.map(middleware => middleware(middlewareAPI));
    
        // compose(...chain)(store.dispatch) 返回了一个function
        // 伪代码如下,
        // function (action) {
        //   middleware(store)(store.dispatch);
        // }
        dispatch = compose(...chain)(store.dispatch);  // 从右到左, middleware1( middleware2( middleware3(dispatch) ) )
    
        // 于是,最终调用 applyMiddleware(...middlewares)(Redux.createStore)
        // 返回的 store, getState,subscribe 方法都是原始的那个 store.getState, store.subscribe
        // 至于dispatch是封装过的
        return {
          ...store,
          dispatch
        };
      };
    }

    export default function applyMiddleware(...middlewares) {
      // --- 传入 createStore,返回增强版的 store
      return (createStore) => (reducer, preloadedState, enhancer) => {
        var store = createStore(reducer, preloadedState, enhancer)
        var dispatch = store.dispatch
        var chain = []
    
        // --- middleware 中的参数
        var middlewareAPI = {
          getState: store.getState,
          dispatch: (action) => dispatch(action)
        }
    
        // --- 给 middleware 传参
        chain = middlewares.map(middleware => middleware(middlewareAPI))
    
        // --- 组合 chain,传入原始的 dispatch
        dispatch = compose(...chain)(store.dispatch)
    
        return {
          ...store, // --- 保留 subscribe, getState, replaceReducer, [$$observable]: observable
          dispatch // --- 用新 dispatch 覆盖,之后调用 dispatch 就会触发 chain 内的 middleware 链式执行
        }
      }
    }

Provider.js

React-Redux是用在连接React和Redux上的。如果你想同时用这两个框架,那么React-Redux基本就是必须的了。


    export default class Provider extends Component {
      getChildContext() {
        // 将其声明为 context 的属性之一
        return { store: this.store }
      }
    
      constructor(props, context) {
        super(props, context)
        // 接收 redux 的 store 作为 props
        this.store = props.store
      }
    
      render() {
        return Children.only(this.props.children)
      }
    }
    
    Provider.propTypes = {
      store: storeShape.isRequired,
      children: PropTypes.element.isRequired
    }
    Provider.childContextTypes = {
      store: storeShape.isRequired
    }

raganyaYoung
445 声望21 粉丝

坚持不断地学习,做一名合格的布道者。