前言
Redux 可以说是前端数据流控制的一大 Boss,根据 Redux 衍生的类库数不胜数,这就说明仅仅掌握 Redux 的使用是不够的,需要深入源码,了解细节为佳,本文将尝试阅读 Redux 的源码,源码本身也不长,更多的是体会这种流管理的思想.
Redux 源码是用 Ts 写的,不熟悉 Ts 的朋友可以先去TypeScript 官网熟悉下
使用
老规矩,看源码之前先熟悉下 Redux 是怎么用的,这当然要去Redux 官网或者Github 仓库去看文档了.(为了避免中文文档没有及时更新和翻译带来的难理解,建议去外文官网)
首先映入眼帘的都是同一句话
Redux is a predictable state container for JavaScript apps.
predictable,可预测的,是redux很关键的概念
总的来说,Redux 数据流很简洁,一个state
、一个action
、一个reducers
,使用上就是编写上述三者,再通过一些内置 API 联系在一起,生成一个store
,通过官网 🧅 说明
1. action
action 就是一个对象
,描述一个可以改变state的事件
,那怎么描述呢?约定是在该对象上定义一个type
属性
const THREEPM = {
type: "DrinkTea",
payload: "xxxxx",
};
就这么简单,至于写成函数返回对象还是异步 action,我不管,whatever
2. state
state 也只是一个对象
,保存程序状态,无任何约束
const state = {
todo:[];
}
就这么简单,至于是写在 reducers 的参数列表了还是配套 immuableJS,我不管,whatever
3. reducers
这名字就起的诡异,还不如叫 actionToState,这家伙的主要作用就是描述 action 触发之后要干嘛,也就是根据 action 更新 state,约束就是每次必须返回新的state,不能修改原来的state
即 (state, action) => newState;
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case "counter/incremented":
return { value: state.value + 1 };
case "counter/decremented":
return { value: state.value - 1 };
default:
return state;
}
}
关联
就这么简单,大道至简,那怎么关联他们呢?更简单,将 reducers 传入 createStore,生成 store 管理他们三个就行了
import { createStore } from "redux";
const store = createStore(reducers);
生成的 store 就是老大,负责三个小弟的通信和管理
根据开头的那张图,具体流程必须是以下这样
store.dispatch(action) -> reducers handle -> update state
至于怎么监听更的 state,怎么进行异步操作,怎么分散 state 到几个 reducers,怎么合并几个 reducers 的 state 成总体 state,这里不再赘述.
createStore
使用部分为我们打开了一个切入点,因为只用到了一个 API,createStore
,所谓的黑魔法就发生在这里.
源码 index.ts
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes,
};
我们先看下,整个库导出的接口型 API,什么叫接口型 API 呢,就是给开发人员用的,一共就 6 个,可以看到,混进了个奇怪的东西 🧞♀️,这个__DO__NOT__USE__ActionTypes
是个什么东西呢?定义如下
源码 utils/actionTypes
const randomString = () =>
Math.random().toString(36).substring(7).split("").join(".");
const ActionTypes = {
INIT: `@@redux/INIT${/* #__PURE__ */ randomString()}`,
REPLACE: `@@redux/REPLACE${/* #__PURE__ */ randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`,
};
export default ActionTypes;
是几个奇奇怪怪的 action type
,这些action type
主要用于内部事件标识,让我们别用,那不用就是了.
说回createStore
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
createStore
有三个重载,返回值都是泛型 Store 和 Ext 的交叉类型,但三个重载的参数并不相同,其中reducer
都是必须的,可选的有enhancer
和preloadedState
enhancer
可选增强器,属于第三方插件性接口,比如说中间件增强等等
preloadedState
可选初始化的state
createStore 内部流程
进入到函数内部,首先 javascript 特色,参数判断,根据不同的参数情况会执行不同的步骤
【有增强器】:
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
【无增强器】:
//1. 内部属性初始化
let currentReducer = reducer
let currentState = preloadedState as S
let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
//2. 内部方法定义
//略
//3. dispatch 一个 action ,就是上面不让我们用的那个action type
dispatch({ type: ActionTypes.INIT } as A)
//4. 生成store
const store = {
dispatch: dispatch as Dispatch<A>,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
} as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
return store
}
dispatch、subscribe
完完全全的观察者模式,subscribe 进行监听,dispatch 去 notify listener,只是在 dispatch 去 notify 之前,把 currentState 传进 reducer 获得最新的 state 而已
dispatch 节选
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
subscribe 节选
nextListeners.push(listener);
真的没有黑魔法 🙉
当然,redux 的核心就是一开始提到的可预测,因为无论是 state 还是 reducer,都存在两个版本,一个 pre,一个 cur,并且每次的 reducer 改变 state,都是立刻改变,并通知更新,并不会累积一下再去统一做改变,这样性能上可能不是最佳的,但是一切都是实打实的预料之中的,设计思想很坚定.
增强器与中间件
增强器
很多朋友知道 redux 有个叫中间件的东西,却不知道其实中间件只是增强器的一种,所谓增强器,就是可以增强 store 功能的高阶函数,在前面 createStore 的参数里作为可选项,下面来看下他的定义
export type StoreEnhancer<Ext = {}, StateExt = never> = (
next: StoreEnhancerStoreCreator<Ext, StateExt>
) => StoreEnhancerStoreCreator<Ext, StateExt>;
可以看到,增强器是接收storeEnhancerStoreCreator
类型返回值也是storeEnhancerStoreCreator
类型,storeEnhancerStoreCreator
类型定义如下
export type StoreEnhancerStoreCreator<Ext = {}, StateExt = never> = <
S = any,
A extends Action = AnyAction
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>
) => Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
而storeEnhancerStoreCreator
则是接收reducer
和preloadedState
作为参数返回store
,因为如果调用createStore
时有传入enhancer
,执行enhancer
并将createStore
作为参数传入.
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
中间件增强器
光说不练假把式,Redux 就内置了一个我们熟悉的增强器applyMiddleware
,先看其使用
import { applyMiddleware } from "redux";
const middleware = [thunk];
const store = createStore(reducer, applyMiddleware(...middleware));
applyMiddleware
有很多个类型重载,都是接收若干个中间件作为参数,返回一个storeEnhancer
,这里只截取其中一个重载
export default function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer<any>;
因为他是先执行再作为增强器传入createStore
,所以返回值自然是个增强器咯
核心代码如下:
const store = createStore(reducer, preloadedState);
let dispatch: Dispatch = () => {
throw new Error(
"Dispatching while constructing your middleware is not allowed. " +
"Other middleware would not be applied to this dispatch."
);
};
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args),
};
const chain = middlewares.map((middleware) => middleware(middlewareAPI));
dispatch = compose<typeof dispatch>(...chain)(store.dispatch);
return {
...store,
dispatch,
};
- 创建出 store
- 把暴露给中间件的 API 写在 middlewareAPI 对象里,传给每个中间件
- 调用
compose
组合全部中间件
compose
的作用是将compose(f, g, h)
转换成(...args) => f(g(h(...args)))
,更多信息在官网中间件章节
尾声
以上就是 Redux 核心代码的解读,本人能力有限,难免出错,望请包涵!
总的来说,Redux 内部实现足够简洁且拓展性很好,这也可能是为什么 base 在其上有这么多库的原因,能读懂才有 PR,接下来本人还会分析几个 Redux 相关库的源码,比如 redux-thunk,redux-saga 等
感谢阅读 🌈