1. 概述
本文带着大家回顾下redux
用法,redux
的API
,createStore
、reducer
、dispatch
等,理解用法后,一起探究原理,难点是redux中间件
的原理部分。
注意:本文只探究redux
,不关注react-redux
,react-redux
的使用和原理后面会有专文讲解,redux
的用处不限于react
,任何你想要订阅事件的地方,都可以单独使用redux
。
2. 设计思想
-
redux
是将整个应用状态存储到到一个地方,称为store
,单一数据源 - 里面保存一棵状态树
state
- 派发
dispatch
行为action
给store
,而不是直接更改state
,只读 - 用户通过订阅(
subscribe
) store,当有dispatch
行为时,会通知订阅者更新
下图是我画的
redux
流程,其实从图中看redux逻辑也简单,你只能用dispatch
通过派发action
更新state
,action
派发后,内部有两个事情处理,一是要调用reducer
更新state
,二是触发之前订阅过的事件执行,就完了。
3. redux基本使用
本文重点讲原理,适合有redux基础的同学,用法简单回顾。
3.1 createStore
用来创建store
import { createStore } from 'redux';
const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
const store = createStore(reducer);
3.2 getState
获取当前state
数据
const state = store.getState();
3.3 action
action
是一个对象。其中的type属性是必须的
,表示 action
的名称。其他属性可以自由设置。
const action = {
type: 'ADD_TODO',
payload: 'Learn Redux'
};
3.3 dispatch
派发action
的唯一途径
import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});
3.4 reducer
接受旧的state
和action
作为参数,返回新的state
const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
3.5 subscribe
设置订阅,dispatch
时会自动执行所有订阅的事件
import { createStore } from 'redux';
const store = createStore(reducer);
let unsubscribe = store.subscribe(() => {
console.log("state更新了")
});
// subscribe的返回值可以取消订阅
// unsubscribe();
3.6 combineReducers
把多个reducer
合并成一个,因为在实际项目中,不同的业务数据不可能全部写在一个reducer里,维护成本高,通常把reducer
按业务分开,最后用combineReducers
合并后,再传给createStore
import { combineReducers } from 'redux';
const reducer = combineReducers({
chatLog,
statusMessage,
userName
})
export default reducer;
4. 原理
我看的redux版本是4.1.2的源码
4.1 createStore源码分析
/**
* 创建store
* @param {*} reducer
* @param {*} preloadedState
* @param {*} enhancer
* @returns
*/
unction createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error(process.env.NODE_ENV === "production" ? formatProdErrorMessage(1) : "Expected the enhancer to be a function. Instead, received: '" + kindOf(enhancer) + "'");
}
return enhancer(createStore)(reducer, preloadedState);
}
// 上部分redux中间件再讲,我们先只关注createStore第一个参数reducer
// reducer必须是个函数
if (typeof reducer !== 'function') {
throw new Error("Expected the root reducer to be a function.");
}
// 定义内部变量
var currentReducer = reducer;
// 当前state数据,初始值是preloadedState
var currentState = preloadedState;
// 保存所有订阅函数
var currentListeners = [];
// 保存的订阅函数快照
var nextListeners = currentListeners;
// 是否dispatch正在执行
var isDispatching = false;
// 为subscribe执行时,提供备份,具体在subscribe函数细看
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
}
/**
* getState源码,获取当前最新state
* @returns 返回state
*/
function getState() {
return currentState;
}
/**
* subscribe源码,用来收集订阅
* @param {*} listener 订阅方法
* @returns 返回取消此订阅的方法
*/
function subscribe(listener) {
// listener 必须是函数
if (typeof listener !== 'function') {
throw new Error("Expected the listener to be a function");
}
// 闭包变量,用来标识取消订阅方法只能执行一次
var isSubscribed = true;
// 为每次订阅提供快照备份nextListeners,
// 防止在dispatch里遍历currentListeners过程中,触发了订阅/取消订阅功能。
// 若直接更新currentListeners将造成当前循环体逻辑混乱,
// 因此所有订阅/取消订阅的listeners都是在nextListeners中存储的,并不会影响当前的dispatch(action)
ensureCanMutateNextListeners();
nextListeners.push(listener);
return function unsubscribe() {
// 已经取消了,直接跳出
if (!isSubscribed) {
return;
}
// 取消成功,没有此订阅了
isSubscribed = false;
ensureCanMutateNextListeners();
var index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
currentListeners = null;
};
}
/**
* dispatch源码
* @param {*} action 接受action对象
* @returns 返回action
*/
function dispatch(action) {
// action是个纯对象
if (!isPlainObject(action)) {
throw new Error("Actions must be plain objects.");
}
// action对象必须有type属性
if (typeof action.type === 'undefined') {
throw new Error('Actions may not have an undefined "type" property. You may have misspelled an action type string constant.');
}
// 执行reducer时,不允许有其他dispatch操作
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
// 开始执行reducer
isDispatching = true;
// reducer接受当前state和action对象,返回新的state
currentState = currentReducer(currentState, action);
} finally {
// reducer执行完成,state更新完毕
isDispatching = false;
}
// 然后再执行所有subscribe订阅过的listeners
var listeners = currentListeners = nextListeners;
// 遍历执行
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}
// 返回action
return action;
}
// 首先派发初始化action,目的是给state赋初始值
// 因为reducer函数,在初始化时,可以传个state初始值,所以在内部调用下,reducer的action没有匹配时,返回默认值
// 注意:如果createStore传了第二个参数是个对象,也代表初始state,见代码:var currentState = preloadedState;
// 此时会覆盖reducer的默认值,因为在调用currentReducer(currentState, action)方式传的currentState就是preloadedState,所以reducer函数的默认值无效了
dispatch({
type: ActionTypes.INIT
});
return {
dispatch: dispatch,
subscribe: subscribe,
getState: getState
}
}
4.2 combineReducers源码解析
对照
combineReducers
使用方法,再看原理
import { combineReducers } from 'redux';
function chatLog(state, action) {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
}
function statusMessage(state, action) {
// 同chatLog
}
function userName(state, action) {
// 同chatLog
}
const reducer = combineReducers({
chatLog,
statusMessage,
userName
})
const store = createStore(reducer);
原理
function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers);
var finalReducers = {};
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
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
// 返回还是reducer函数的类型
// dispatch实际执行的reducer
return function combination(state, action) {
// state没有默认值时,初始化一个对象
if (state === void 0) {
state = {};
}
// 标识state中每个key的值更新前后是否有变化
var hasChanged = false;
// 更新之后的state
var nextState = {};
// 遍历每个小的reducer
for (var _i = 0; _i < finalReducerKeys.length; _i++) {
// combineReducers参数对象的key
var _key = finalReducerKeys[_i];
// key对应的reducer
var reducer = finalReducers[_key];
// 通过 combineReducers参数对象的key 取出之前的状态
var previousStateForKey = state[_key];
// 每个reducer都会调用
var nextStateForKey = reducer(previousStateForKey, action);
// 保存每个key的返回的新的state
nextState[_key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
// 有变化返回新的state,没变化返回旧的state
return hasChanged ? nextState : state;
};
}
总结下
-
combineReducers
的返回值还是个reducer形式的函数
- 因为
createStore
方法内,会初始执行一次dispatch
,所以源码中combination
(组合后的reducer
)会首先调用一次,返回与combineReducers
参数对象一样结构的state
,即state
的结构同combineReducers
参数对象,对象的属性值是每个reducer
对应的返回值 - 有
dispatch
派发时,所有的reducer
都会执行一次,reducer
接收的是state
对应key
的value
,不是整个state
状态数据
5. 结尾
到这redux原理就完了,其实发现redux源码简单,核心就是createStore
方法。
一句话描述redux原理:通过subscribe订阅更新,dispatch改变state时遍历执行所有的订阅事件。
redux中间后面会写个专文来讲解,本篇文章内容不少了,不放在本篇了,先理解好redux原理。
完
如有错误,请指正,欢迎评论交流,关注我,只写干货