Redux是一个常用的组件状态管理库,通过将应用的状态数据集中存储并且限制可以更改状态数据的路径,使得开发者可以更好的追踪以及预测应用状态的变化过程,Redux的源代码不到1000行,是一个学习源代码分析的不错对象.
在浏览器中通过script
标签加载后,脚本会在全局作用域下添加Redux
变量.
对应的源代码如下
/**
* 先探测脚本所处的运行环境和加载机制,然后将redux的方法和属性挂载到对应的作用域
* 1. Node.js环境下和AMD加载机制下挂载到模块的导出对象
* 2. script标签加载挂载到window对象
*/
(function (global, factory) {
// Node.js环境
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
// 浏览器环境(通过AMD方式加载)
typeof define === 'function' && define.amd ? define(['exports'], factory) :
// 浏览器环境(通过script标签加载)
(global = global || self, factory(global.Redux = {}));
}(this, function (exports) {
/* other code... */
exports.__DO_NOT_USE__ActionTypes = ActionTypes;
exports.applyMiddleware = applyMiddleware;
exports.bindActionCreators = bindActionCreators;
exports.combineReducers = combineReducers;
exports.compose = compose;
exports.createStore = createStore;
Object.defineProperty(exports, '__esModule', { value: true });
}));
__DO_NOT_USE__ActionTypes
/**
* 生成以小数点分割的随机数
*/
var randomString = function randomString() {
return Math.random().toString(36).substring(7).split('').join('.');
};
/**
* 这些action type是redux内部的保留关键字
* 不可以在业务代码中使用这些action type
*/
var ActionTypes = {
INIT: "@@redux/INIT" + randomString(),
REPLACE: "@@redux/REPLACE" + randomString(),
PROBE_UNKNOWN_ACTION: function PROBE_UNKNOWN_ACTION() {
return "@@redux/PROBE_UNKNOWN_ACTION" + randomString();
}
};
compose
/**
* compose是一个工具函数,它接收一系列的函数作为数组,并且将这些函数形成一个调用栈,其中最右侧的函数位于栈顶,最左侧的函数位于栈底
* 栈顶的函数可以接收任意个参数,并且将其返回值作为下一个函数的参数,以此类推,最终栈底的函数的返回值为最终的结果
* @param {Array<Function>} functions 函数数组
* @returns {Function}
*/
function compose(func1, func2, func3) {
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];
}
return funcs.reduce(function (a, b) {
return function () {
return a(b.apply(void 0, arguments));
};
});
}
createStore
createStore
用于创建store,store就是redux用于存储应用状态数据的地方,其本质是一个对象,store拥有getState
方法,用来获取应用状态数据,dispatch
方法用来触发reducer的计算从而更新应用状态,subscribe
方法用于订阅状态更新的处理函数,每次调用dispatch
后redux都会执行这些函数.
/**
* 创建store
* @param {Function} reducer reducer函数用于计算state,每一次调用store.dispatch,redux都会调用reducer函数并传入当前的state和对应的action,
* reducer函数会根据action的type属性和payload计算最新的state,对于未匹配到的action类型,reducer函数应该返回默认的或者当前的state,
* reducer函数不能返回一个undefined值,但是可以返回null,因为一个应用任何时刻都有一个确定的状态,不可能是未定义的
* @param {String} preloadedState preloadedState是一个序列化的对象字符串,用于表示默认的state,如果应用使用了服务端渲染,那么在浏览器端创建store的时候,
* 应该将在服务端计算出的state经过序列化后传给preloadState
* @param {Function} enhancer enhancer是一个高阶函数,以createStore函数为参数,并返回增强的'enhancedCreateStore'函数,开发者可以用enhancer给createStore函数
* 添加额外的处理逻辑
* @returns {Object}
*/
function createStore(reducer, preloadedState, enhancer) {
var _ref2;
/**
* 参数有效性检查
* enhancer函数只能有一个
*/
if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3] === 'function') {
throw new Error('It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function.');
}
// 参数容错处理
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}
// enhancer必须得是一个函数
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.');
}
// 调用enhancer函数并用增强版的'createStore'函数创建store
return enhancer(createStore)(reducer, preloadedState);
}
// reducer必须得是一个函数
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.');
}
// 存储当前的reducer函数
var currentReducer = reducer;
// 存储应用的当前state
var currentState = preloadedState;
// 存储目前存在的状态更新订阅函数
var currentListeners = [];
var nextListeners = currentListeners;
// 表示应用state此刻是否正处于被更新中,防止两个更新操作同时进行
var isDispatching = false;
/**
* 数组浅拷贝,使得对currentListeners数组的变更更安全
*/
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
}
/**
* 回去应用的当前state
* @returns {Object}
*/
function getState() {
if (isDispatching) {
throw new Error('You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.');
}
return currentState;
}
/**
* 订阅一个state发生更新的处理函数
* @param {Function} listener
* @returns {Function} 执行此函数可以取消当前订阅
*/
function subscribe(listener) {
// 参数校验,listener必须是一个函数
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.');
}
// 不能在状态更新(reducer函数执行)的过程中订阅处理函数
if (isDispatching) {
throw new Error('You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.');
}
// 函数订阅成功
var isSubscribed = true;
// 函数入队
ensureCanMutateNextListeners();
nextListeners.push(listener);
return function unsubscribe() {
if (!isSubscribed) {
return;
}
// 不能在状态更新(reducer函数执行)的过程中取消订阅
if (isDispatching) {
throw new Error('You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.');
}
isSubscribed = false;
ensureCanMutateNextListeners();
var index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}
/**
* dispatch函数是redux中唯一可以更新state的途径,结合dispatch和reducer,开发者可以对应用状态进行记录、预测、重放等操作
* @param {Object} action action必须有一个type属性,用于指示reducer如何计算最新的state
*/
function dispatch(action) {
// action是一个对象,但不能是数组、NULL等值
if (!isPlainObject(action)) {
throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
}
// action必须存在一个明确的type属性
if (typeof action.type === 'undefined') {
throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
}
// 不能同时存在两个state更新进程,否则哪个进程优先级更高呢?
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
// 锁住,此次更新结束前后续所有的更新操作都丢弃
isDispatching = true;
// 调用reducer函数计算最新的state,reducer函数的参数为当前state和对应的action
currentState = currentReducer(currentState, action);
} finally {
// 更新完毕,释放锁
isDispatching = false;
}
// 更新完毕,按订阅顺序依次执行处理函数
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}
// 返回action,提供给redux中间件进行后续处理
return action;
}
/**
* 应用运行期间更换reducer函数
* @param {Function} nextReducer
*/
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.');
}
currentReducer = nextReducer;
// REPLACE action和INIT action的目的一样,是为了计算默认的state
dispatch({
type: ActionTypes.REPLACE
});
}
/**
* 提供一种机制供类似rx.js这样的observable/reactive库使用
*/
function observable() {
var _ref;
var outerSubscribe = subscribe;
return _ref = {
subscribe: function subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.');
}
function observeState() {
if (observer.next) {
observer.next(getState());
}
}
observeState();
var unsubscribe = outerSubscribe(observeState);
return {
unsubscribe: unsubscribe
};
}
}, _ref[result] = function () {
return this;
}, _ref;
}
// redux内部dispatch一个INIT的action,目的是为了计算应用最初的默认state
dispatch({
type: ActionTypes.INIT
});
return _ref2 = {
dispatch: dispatch,
subscribe: subscribe,
getState: getState,
replaceReducer: replaceReducer
}, _ref2[result] = observable, _ref2;
}
combineReducers
combineReducers
函数用于组合reducer,在大型web应用中,state的结构比较复杂,对应reducer函数也比较庞大,出于提升可维护性的目的,按照业务逻辑对state进行拆分成多个子state,每个子state对应一个子reducer函数,在创建store的时候,先调用combineReducers
函数将各个子reducer函数组合成一个总体的reducer函数.
/**
* combineReducers函数的使用例子
*/
const { combineReducers, createStore } = Redux;
// trivial child reducer
function reducerA(state, action) {
return 'a';
}
// another trivial child reducer
function reducerB(state, action) {
return 'b';
}
// attention: 应用state最终也拥有childA和childB两个属性
const rootReducer = combineReducers({
childA: reducerA,
childB: reducerB
});
const store = createStore(rootReducers);
/**
* 检查各个子reducer函数是否会返回undefined值,前面说了,reducer函数可以返回null,但是不能返回undefined
*/
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(function (key) {
var reducer = reducers[key];
// dispatch INIT action
var initialState = reducer(undefined, {
type: ActionTypes.INIT
});
if (typeof initialState === 'undefined') {
throw new Error("Reducer \"" + key + "\" returned undefined during initialization. " + "If the state passed to the reducer is undefined, you must " + "explicitly return the initial state. The initial state may " + "not be undefined. If you don't want to set a value for this reducer, " + "you can use null instead of undefined.");
}
// dispatch一些其他的action再确认一下reducer的合法性
if (typeof reducer(undefined, {
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === 'undefined') {
throw new Error("Reducer \"" + key + "\" returned undefined when probed with a random type. " + ("Don't try to handle " + ActionTypes.INIT + " or other actions in \"redux/*\" ") + "namespace. They are considered private. Instead, you must return the " + "current state for any unknown actions, unless it is undefined, " + "in which case you must return the initial state, regardless of the " + "action type. The initial state may not be undefined, but can be null.");
}
});
}
/**
* 检查应用当前的state和reducers的对象结构是否一致,如果不一致,控制台给出警告提示
* @param {Object} inputState 应用当前的state
* @param {Object} reducers reducers对象
* @param {action} action 当前被dispatch的action
* @param {Object} unexpectedKeyCache 应用运行过程中state和reducers之间不一致的key都会缓存在这里
*/
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
var reducerKeys = Object.keys(reducers);
var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer';
if (reducerKeys.length === 0) {
return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.';
}
if (!isPlainObject(inputState)) {
return "The " + argumentName + " has unexpected type of \"" + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + "\". Expected argument to be an object with the following " + ("keys: \"" + reducerKeys.join('", "') + "\"");
}
// 正常情况下,应用state中有的属性,reducers对象中也要有
var unexpectedKeys = Object.keys(inputState).filter(function (key) {
return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key];
});
unexpectedKeys.forEach(function (key) {
unexpectedKeyCache[key] = true;
});
if (action && action.type === ActionTypes.REPLACE) return;
if (unexpectedKeys.length > 0) {
return "Unexpected " + (unexpectedKeys.length > 1 ? 'keys' : 'key') + " " + ("\"" + unexpectedKeys.join('", "') + "\" found in " + argumentName + ". ") + "Expected to find one of the known reducer keys instead: " + ("\"" + reducerKeys.join('", "') + "\". Unexpected keys will be ignored.");
}
}
/**
* 组合reducer函数
* @param {Object} reducers reducers是一个对象,对象的key对应各个子state的key,value为各个子state对应的reducer函数
* @returns {Function} 最终组合而成的应用reducer函数
*/
function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers);
var finalReducers = {};
// 复制reducers对象到闭包变量中
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
{
// 错误检测,剔除无效的子reducer
if (typeof reducers[key] === 'undefined') {
warning("No reducer provided for key \"" + key + "\"");
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key];
}
}
var finalReducerKeys = Object.keys(finalReducers); // This is used to make sure we don't warn about the same
// keys multiple times.
var unexpectedKeyCache;
{
unexpectedKeyCache = {};
}
var shapeAssertionError;
try {
// 检查各个reducer是否合法
assertReducerShape(finalReducers);
} catch (e) {
shapeAssertionError = e;
}
return function combination(state, action) {
// state默认是一个空对象
if (state === void 0) {
state = {};
}
// 存在不合法的reducer,抛出错误
if (shapeAssertionError) {
throw shapeAssertionError;
}
{
// 检测state和reducers之间的不一致
var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
if (warningMessage) {
warning(warningMessage);
}
}
// 这个变量指示dispatch后应用state是否确实发生了改变
var hasChanged = false;
var nextState = {};
// 依次执行每个子reducer函数,参数为对应的子state和action,返回值为新的子state
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的返回值不能是undefined
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(_key, action);
throw new Error(errorMessage);
}
nextState[_key] = nextStateForKey;
// attention: reducer函数在更新state的过程中,必须要返回一个新的对象,不能直接对参数state做更改后再返回,原因看下面
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
// 原因在这里,如果每个reducer都不返回新的对象,那么redux返回的是旧的应用state,最后应用状态未得到更新,此次dispatch操作就默默的失败了,无任何告警提示
return hasChanged ? nextState : state;
};
}
applyMiddleware
applyMiddleware
函数用于给redux添加中间件,所谓redux中间件,本质上是一个函数,用于拦截redux的dispatch函数调用并且添加自定义操作,但是最终依然会调用redux的dispatch方法更新state,例如redux-thunk
这个常用的中间件,使得开发者可以dispatch一个函数类型的action.
/**
* redux-thunk中间件源代码,就是一个简单的函数
*/
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// next就是redux的dispatch函数
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
/**
* applyMiddleware函数
* @param {Function} middleware 需要注意的是,向applyMiddleware函数传递middleware参数的时候,最终调用redux dispatch函数的中间件
* 要放在参数列表的末尾
*/
function applyMiddleware() {
// 将所有中间件存入一个数组
for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
middlewares[_key] = arguments[_key];
}
// 返回一个高阶函数,函数参数为redux的createStore函数
return function (createStore) {
// 返回一个函数,最终调用此函数创建redux store
return function () {
// 先创建redux store
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.');
};
var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch() {
return _dispatch.apply(void 0, arguments);
}
};
// 每个中间件本质上都是高阶函数,先把redux的getState函数和一个暂时的dispatch函数传进中间件函数的闭包上下文
var chain = middlewares.map(function (middleware) {
return middleware(middlewareAPI);
});
/*
* 调用compose函数创建一个中间件的包装函数(前文有述),然后使用redux的dispatch函数作为参数调用这个包装函数,最后返回重载后的
* dispatch函数,然后替换store对象上原本的dispatch函数,如此一来,业务代码中每次调用`store.dispatch`的时候,redux内部都会
* 按照参数传递的顺序一次执行对应中间件的功能,最终再调用redux本身的dispatch函数更改应用state
*/
_dispatch = compose.apply(void 0, chain)(store.dispatch);
return _objectSpread2({}, store, {
dispatch: _dispatch
});
};
};
}
bindActionCreator
bindActionCreator
是redux提供的一个包装函数,用于将返回一个action
的函数和dispatch
组合起来.
/**
* bindActionCreator函数,将计算action的函数和dispatch进行组合
* @param {Function} actionCreator 返回值为action的函数
* @param {Function} dispatch dispatch函数
* @returns {Function} 开发者执行此函数就直接dispatch了对应的action,不需要每次手动引用redux的dispatch函数
*/
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments));
};
}
bindActionCreators
bindActionCreators
基于bindActionCreator
进行批量包装.
/**
* @param {Function|Object} actionCreators 可以返回action的函数集合
* @param {Function} dispatch dispatch函数
* @returns {Function|Object}
*/
function bindActionCreators(actionCreators, dispatch) {
// 参数为单个函数,与bindActionCreator无异
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch);
}
// 参数校验
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\"?");
}
var boundActionCreators = {};
// 逐个函数进行包装
for (var key in actionCreators) {
var actionCreator = actionCreators[key];
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
}
}
// 返回最后的包装函数集合
return boundActionCreators;
}