Redux 源码阅读记录
一,背景
Redux在mobx之前出现,redux基于Elm, flux, Immutable.js 的思想对状态管理重新做了优化。
在项目中使用时,Redux对数据更新和管理,可以很容易扩展插件,中间件等。
二,redux提供的api
1,compose
对传入的函数进行从右往左的方式编译,函数的合并。
代码实现如下:
/** * 从右往左,编译单个参数的函数。最右函数可以是多个参数的函数,提供单个编译函数。 * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left.
* For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */ function compose() { for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } if (funcs.length === 0) { return function (arg) { return arg; }; } if (funcs.length === 1) { return funcs[0]; } // reduce 多次回归调用,函数是一个包裹的过程,从左往右包裹,执行时从右往左执行。
// 最后返回一个可以执行所有函数的 函数! return funcs.reduce(function (a, b) { return function () { return a(b.apply(void 0, arguments)); }; }); }
2,bindActionCreator / bindActionCreators
将action和dispatch合并,作为action的creator。
// 源码这里 调用函数会返回一个函数,函数执行会返回dispatch执行action的结果,相当于提前执行了dispatch。
function bindActionCreator(actionCreator, dispatch) { return function () { return dispatch(actionCreator.apply(this, arguments)); }; }
bindActionCreator 是为了提前获取dispatch action的结果,还提供了bindActionCreators
/** *
* 转换每一个action creator,将其返回的对象的key都进行 dispatch 包裹,方便直接调用。
* 为了方便,也可以在creator 的第一个参数传入dispatch,用dispatch包裹后再返回。 * * @param {Function|Object} actionCreators An object whose values are action * creator functions. One handy way to obtain it is to use ES6 `import * as` * syntax. You may also pass a single function.
* 和es6 的 import * as 语法结合使用,传入对象或者函数。 * * @param {Function} dispatch The `dispatch` function available on your Redux * store. * */ function bindActionCreators(actionCreators, dispatch) {
// 判断是否是函数 if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch); }
// 判断是否是不是null的对象 if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error("bindActionCreators expected an object or a function, instead received " + (actionCreators === null ? 'null' : typeof actionCreators) + ". " + "Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?"); } // 定义新对象,存储传参对象调用 bindActionCreator 的返回值。 var boundActionCreators = {}; // 如果是对象执行 for-in 循环(for-in专用于遍历对象的key。) for (var key in actionCreators) { var actionCreator = actionCreators[key]; // 对每个对象中的function都调用一次上面的 bindActionCreator() 函数。 if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } } // 返回新的对象 return boundActionCreators; }
3,applyMiddleware
应用中间件,用于丰富redux的功能。
一般写法是这样的。
const composeEnhancers = composeWithDevTools({ actionCreators, trace: true, traceLimit: 25 }); const store = createStore(reducer, preloadedState, composeEnhancers( // 引入中间件的方式
applyMiddleware(invariant(), thunk)
));
return store; ));
查看这个中间件需要先看一个关键的函数:_objectSpread2
_objectSpread2 函数代码如下
// es6 的方式把对象的key
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) {
// push.apply 数组的合并,获取可枚举的key + 符号类型的key(是否可枚举都获取到),得到完整的key数组。 keys.push.apply(keys, Object.getOwnPropertySymbols(object)); } // 过滤掉不能枚举的属性 if (enumerableOnly) keys = keys.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); return keys; // 返回对象的key数组 }
// redux加载中间件 关键的函数
// function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {};
// 如果资源的下标是偶数 if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
以下是 applyMiddleware 源码的实现:
/** * * 引用中间件以丰富修改 redux store 的dispatch方法。
* 满足不同的任务,用简明的方式实现,例如:异步action,日志等。
* * 参考 `redux-thunk` 就是处理异步的中间件。 * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain.
* 因为中间件是潜在的异步过程,应该是在组和链中第一个被调用 * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments.
* 每个中间件都应该有 dispatch 和 getState 方法。 * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware.
* 然后返回应用中间件的store enhancer 状态值的放大器。
* 重点意思是,对action的再次封装返回新的action。 */ function applyMiddleware() {
// 遍历函数arguments,并将参数重新收集到一个新的 middlewares 数组。 for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } return function (createStore) { return function () { var store = createStore.apply(void 0, arguments);
// 引用中间件不被允许的提示。 var _dispatch = function dispatch() { throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.'); };
// store的 getState dispatch 方法,拿过来给每个中间件都是 var middlewareAPI = { getState: store.getState, dispatch: function dispatch() { return _dispatch.apply(void 0, arguments); } }; var chain = middlewares.map(function (middleware) {
// 将getState和dispatch传递给中间件 return middleware(middlewareAPI); });
//把添加getState和dispatch的中间件数组,调用 compose 进行一次dispatch的合并,返回一个函数再次调用得到新的dispatch。 _dispatch = compose.apply(void 0, chain)(store.dispatch); return _objectSpread2({}, store, { dispatch: _dispatch }); }; }; }
4,composeEnhancers