深入redux源码 (1)

作者: immutable | 来源:发表于2016-08-07 16:34 被阅读514次

    redux 的安装

    npm install redux --save
    

    redux 有五个js构成,纯函数实现


    因为 redux 是纯函数,所以很方便进行测试.在webStorm新建个node环境,安装redux,就可以跑redux的例子.

    先看个最简单的使用例子:

    /**
     * Created by lijie on 16/8/7.
     */
    // 首先定义一个改变数据的plain函数,成为reducer
    'use strict';
    function count (state, action) {
        var defaultState = {
            year: 2015,
        };
        state = state || defaultState;
        switch (action.type) {
            case 'add':
                return {
                    year: state.year + 1
                };
            case 'sub':
                return {
                    year: state.year - 1
                }
            default :
                return state;
        }
    }
    
    // store的创建
    var createStore = require('redux').createStore;
    var store = createStore(count);
    
    // store里面的数据发生改变时,触发的回调函数
    store.subscribe(function () {
        console.log('the year is: ', store.getState().year);
    });
    
    // action: 触发state改变的唯一方法(按照redux的设计思路)
    var action1 = { type: 'add' };
    var action2 = { type: 'add' };
    var action3 = { type: 'sub' };
    
    // 改变store里面的方法
    store.dispatch(action1); // 'the year is: 2016
    store.dispatch(action2); // 'the year is: 2017
    store.dispatch(action3); // 'the year is: 2016
    

    运行结果如下:


    该例子只用到了一个API: createStore, 先看这个主干 createStore.js

    createStore.js

    参数

    function createStore(reducer, initialState, enhancer) {
        ...
    }
    

    reducer 示例中的counter函数,作用是根据action处理state的变化

    initialState 初始化的 state 状态树

    enhancer 增强函数,需要用applyMiddleware 函数加入自定义的功能.
    例如 常用第三方库redux-thunkredux-logger

    subscribe 函数

      function subscribe(listener) {
        if (typeof listener !== 'function') {
          throw new Error('Expected listener to be a function.');
        }
    
        var isSubscribed = true;
    
        ensureCanMutateNextListeners();
        nextListeners.push(listener);
    
        return function unsubscribe() {
          if (!isSubscribed) {
            return;
          }
    
          isSubscribed = false;
    
          ensureCanMutateNextListeners();
          var index = nextListeners.indexOf(listener);
          nextListeners.splice(index, 1);
        };
      }
    

    listener 表示观察者函数
    nextListeners: 最新的listener 数组

    该函数在做的事情: 将之前的listener数组复制给nextListener,并将当前的注册的listener也加入nextListener中

    返回的是 清除当前观察者对象的函数 ,该函数就将当前listener从数组中又拿掉了.以后再通过dispatch发送action时,该listener将不会通知该函数了.

    dispatch 函数

      function dispatch(action) {
    
        if (!(0, _isPlainObject2["default"])(action)) {
          throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
        }
    
        if (typeof action.type === 'undefined') {
          throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
        }
    
        if (isDispatching) {
          throw new Error('Reducers may not dispatch actions.');
        }
    
        try {
          isDispatching = true;
          currentState = currentReducer(currentState, action);
        } finally {
          isDispatching = false;
        }
    
        var listeners = currentListeners = nextListeners;
        for (var i = 0; i < listeners.length; i++) {
          listeners[i]();
        }
    
        return action;
      }
    

    前面一长串方法都是判断发送的Action是否是 对象,actionType是否是 undefined ,是否已经发送过

    最关键部分 currentState = currentReducer(currentState, action);

    currentReducer 就是初始化赋值给的reducer函数
    在dispatch 函数中给到各个reducer进行处理

    最后一步 for 循环通知当前所有的观察者

    这样的话,示例中所运行的效果就能解释通了.

    redux 中间件

    redux 库能衍生出丰富的工具集和可拓展的生态系统

    为刚才的示例添加一个 监控数据变化的 logger 的中间件

    'use strict';
    
    const logger = store => next => action => {
        console.log("pre---"+JSON.stringify(store.getState()));
        const result = next(action);
        console.log("end---"+JSON.stringify(store.getState()));
        return result;
    };
    
    function count (state, action) {
        var defaultState = {
            year: 2015,
        };
        state = state || defaultState;
        switch (action.type) {
            case 'add':
                return {
                    year: state.year + 1
                };
            case 'sub':
                return {
                    year: state.year - 1
                }
            default :
                return state;
        }
    }
    
    // store的创建
    var createStore = require('redux').createStore;
    var applyMiddleware=require('redux').applyMiddleware;
    
    const createStoreWithMiddleware = applyMiddleware(logger)(createStore);
    var store=createStoreWithMiddleware(count,undefined);
    
    // store里面的数据发生改变时,触发的回调函数
    store.subscribe(function () {
        console.log('the year is: ', store.getState().year);
    });
    // action: 触发state改变的唯一方法(按照redux的设计思路)
    var action1 = { type: 'add' };
    
    // // 改变store里面的方法
    store.dispatch(action1); // 'the year is: 2016
    

    运行结果如下:


    实现该功能 主要用到了applyMiddleware.js的API

    applyMiddleware.js

    function applyMiddleware() {
      //在这里 找到所有的 第三方中间件 函数
      for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
        middlewares[_key] = arguments[_key];
      }
    
      return function (createStore) {
        return function (reducer, initialState, enhancer) {
          //创建store
          var store = createStore(reducer, initialState, enhancer);
          var _dispatch = store.dispatch; //拿到store的dispatch函数
          var chain = [];
    
          var middlewareAPI = {
            getState: store.getState,
            dispatch: function dispatch(action) {
              return _dispatch(action);
            }
          };
          //将middlewareAPI 作为第三方中间件 参数,并返回 该所有第三方中间件的函数数组
          chain = middlewares.map(function (middleware) {
            return middleware(middlewareAPI);
          });
          //从右到左, middleware1( middleware2( middleware3(dispatch) ) )
          _dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
    
          return _extends({}, store, {
            dispatch: _dispatch
          });
        };
      };
    }
    

    该段代码的作用: 创建store,将中间件转成数组,将store的state与dispatch给到中间件,并嵌套执行.

    在这里面有个compose.js的api使用.官方解释作用是 (...args) => f(g(h(...args))).将数组依次嵌套执行.

    compose.js

    为了更清楚理解compose.js的作用,我们再添加一个 第三方中间件.

    'use strict';
    
    const logger = store => next => action => {
        console.log("pre---"+JSON.stringify(store.getState()));
        const result = next(action);
        console.log("end---"+JSON.stringify(store.getState()));
        return result;
    };
    /**
     * 用 { meta: { delay: N } } 来让 action 延迟 N 毫秒。
     * 在这个案例中,让 `dispatch` 返回一个取消 timeout 的函数。
     */
    const timeoutScheduler = store => next => action => {
        if (!action.meta || !action.meta.delay) {
            return next(action)
        }
    
        let timeoutId = setTimeout(
            () => next(action),
            action.meta.delay
        )
    
        return function cancel() {
            clearTimeout(timeoutId)
        }
    }
    
    
    function count (state, action) {
        var defaultState = {
            year: 2015,
        };
        state = state || defaultState;
        switch (action.type) {
            case 'add':
                return {
                    year: state.year + 1
                };
            case 'sub':
                return {
                    year: state.year - 1
                }
            default :
                return state;
        }
    }
    
    // store的创建
    var createStore = require('redux').createStore;
    var applyMiddleware=require('redux').applyMiddleware;
    
    const createStoreWithMiddleware = applyMiddleware(logger,timeoutScheduler)(createStore);
    var store=createStoreWithMiddleware(count,undefined);
    
    // store里面的数据发生改变时,触发的回调函数
    store.subscribe(function () {
        console.log('the year is: ', store.getState().year);
    });
    // action: 触发state改变的唯一方法(按照redux的设计思路)
    var action1 = { type: 'add', meta: { delay: 2000 }};
    
    // // 改变store里面的方法
    store.dispatch(action1); // 'the year is: 2016
    

    添加的中间件 作用是发送带timeout的action.运行代码,你会发现结果并没有变化,只是listener 得到消息的时间推迟了2000毫秒,但不同的是,logger是实时性的.

    所以compose.js让中间件的执行顺序是 timeoutScheduler( logger(dispatch) )的.

    把使用的顺序颠倒一下:

    const createStoreWithMiddleware = applyMiddleware(timeoutScheduler,logger)(createStore);
    

    将示例的代码改成这样. 你会发现 logger 执行顺序变慢了2000毫秒.
    因为现在的执行是: logger( timeoutScheduler(dispatch) )

    所以可以得出结论: compose.js 中执行顺序是 数组中排在前面的方法最先执行,执行完毕后将 dispatch函数 传递给后一个执行. 并且在前面执行的中间件将影响其后的中间件.


    相关链接

    官方gitbook: http://cn.redux.js.org/docs/advanced/Middleware.html

    解读redux工作原理: http://blog.csdn.net/jhqdlove/article/details/51940400

    Redux系列x:http://www.cnblogs.com/dh-dh/p/5350352.html

    相关文章

      网友评论

      • 7a1f2a2f0fab:我使用react native + firebase. 这样的情况下再使用redux有带来什么好处吗?
        我一直搞不懂为什么要用redux
        能不能解释一下react native + firebase 和 redux react native + firebase 有什么不同

      本文标题:深入redux源码 (1)

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