前言
Redux 可以说是前端数据流控制的一大 Boss,根据 Redux 衍生的类库数不胜数,这就说明仅仅掌握 Redux 的使用是不够的,需要深入源码,了解细节为佳,本文将尝试阅读 Redux 的源码,源码本身也不长,更多的是体会这种流管理的思想.
Redux 源码是用 Ts 写的,不熟悉 Ts 的朋友可以先去TypeScript 官网熟悉下
使用
老规矩,看源码之前先熟悉下 Redux 是怎么用的,这当然要去Redux 官网或者Github 仓库去看文档了.(为了避免中文文档没有及时更新和翻译带来的难理解,建议去外文官网)
首先映入眼帘的都是同一句话
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 等
感谢阅读 ?
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!