继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

深入redux源码 (1)

PIPIONE
关注TA
已关注
手记 921
粉丝 147
获赞 701

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: 2016store.dispatch(action2); // 'the year is: 2017store.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函数 传递给后一个执行. 并且在前面执行的中间件将影响其后的中间件.



作者:immutable
链接:https://www.jianshu.com/p/0677c5af4d5c


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP