一、三大原则
可以说redux的核心设计理念,都是基于单一数据源
、state 状态只读
、使用纯函数来修改 state
的三大原则,所以在分析redux的原理前,需要先理解这三大原则,以及相互之间的关系。
1. 单一数据源
整个应用的 state 会存储在单一的
store
的 Object Tree 中
该设计是由 Flux 架构而来,在 Flux 架构中是允许多个 store 的结构,Redux 简化为只有单一个
整个应用存储在一个 Object Tree 上,可能会导致数据对象过于庞大的问题,Redux 提供了
combineReducers
可以合并多个reducer
, 因此,Store 可以通过拆分多个reducer
来避免数据源对象过于庞大的问题Redux 的单一个 store 的设计有一些好处,对开发者来说,它可以容易调试与观察状态的变化,状态存储于对象树状结构中,也很容易做到重做/复原的功能
2. state 是只读的
唯一修改 state 的方法就是触发
action
不能直接修改
store
所记录的状态值,只能"间接"地通过store.dispatch(action)
发出,再经过reducer
处理返回新的 state(没有直接修改应用状态),从而实现更动 state 的目的state 只读也是借鉴了 Flux 的单向数据流的原则,这样设计的好处是数据更容易追踪,问题容易定位
3. 使用纯函数来修改 state
通过定义
reducer
来确定状态的修改,而每一个reducer
都是纯函数
借鉴了函数式编程,Redux 通过纯函数
reducer
来修改状态,由reducer
所产生的新状态,并不是直接修改之前的状态而来,而是一个复制之前状态,然后加上动作改变的部份,产生的一个新的对象reducer
修改状态,可以简化理解为之前的状态 + 动作 = 新的状态
,公式如下:
(previousState, action) => newState
- 纯函数的好处是没有副作用,相同的输入,永远都会有相同的输出。这样做的好处是这个函数可以不依赖外部环境,更加解耦,更容易调试以及更容易的写单元测试。
二、Action
action
是一个描述"发生了什么事"的纯对象
三、Reducer
四、Store
Redux 提供的
createStore
方法会根据reducer
生成store
。应用中有且只有一个store
createStore.js
1. getState
获取整个应用中
store
管理的 state
function getState() {
// 如果reducer正在执行,报错
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.'
);
}
// 返回整个应用中 store 管理的 state
return currentState;
}
2. dispatch
通过
reducer
根据action
触发状态更新
函数流程图:
源代码:
function dispatch(action) {
// 判断参数action是否是纯对象,不是报错
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?'
);
}
// 如果reducer正在执行,报错
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
// 设置isDispatching为true,防止后续的action进来触发reducer操作
isDispatching = true;
// 根据action触发状态更新
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 当前状态设置完毕后,执行监听函数
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[I];
listener();
}
// 返回参数
return action;
}
3. subscribe
绑定监听函数,
reducer
执行完后执行该函数
function subscribe(listener) {
// 如果参数 listener 不是function,则报错
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.'
);
}
let 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();
// 找到当前的 listener 的位置并删除它
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}
4. replaceReducer
替换
reducer
,且初始化 state
function replaceReducer(nextReducer) {
// 如果参数 nextReducer 不是纯函数,则报错
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.');
}
// 替换 Reducer
currentReducer = nextReducer;
// This action has a similiar effect to ActionTypes.INIT.
// Any reducers that existed in both the new and old rootReducer
// will receive the previous state. This effectively populates
// the new state tree with any relevant data from the old one.
dispatch({ type: ActionTypes.REPLACE });
}
5. observable
function observable() {
const outerSubscribe = subscribe;
return {
subscribe(observer) {
// 如果参数 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();
const unsubscribe = outerSubscribe(observeState);
return { unsubscribe };
},
[$$observable]() {
return this;
},
};
}
五、数据流
严格的
单向数据流
是 Redux 架构的设计核心
Redux 应用中数据的生命周期遵循下面 4 个步骤: