首页 > 技术文章 > Redux 源码阅读记录

the-last 2019-10-08 11:29 原文

一,背景

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

 

推荐阅读