Redux 源码阅读
- V5.0.0 Beta
- 第一次阅读源码
createStore
Redux 核心逻辑就是createStore
代码非常简单
function createStore(reducer, preloadedState, enhancer){
// 一些判断,保证参数正确
/* 定义的变量,后面定义的函数都可以访问这些变量 */
// reducer函数
let currentReducer = reducer
// 状态: store中的状态都保存在这里
let currentState = preloadedState
// 调用subscribe后,就存在这里
let currentListeners = new Map()
let nextListeners = currentListeners
let listenerIdCounter = 0
// 标识是不是正在执行dispatch()
// true状态下,不能够再dispatch, 例如reducer函数中dispatch。
// true状态下,不能subscribe/unsubscribe,不能getState
let isDispatching = false
/* 主要的的函数 */
function getState(){
return currentState
}
function subscribe(listerner){
// dispatch subscribe 实现了发布订阅模式
}
function dispatch(action){
isDispatching = true
// 计算下一个状态
currentState = currentReducer(currentState, action)
isDispatching = false
const listeners = (currentListeners = nextListeners)
// 触发listener
listeners.forEach(listener => {
listener()
})
}
function replaceReducer(){
// 替换reducer
}
function observable(){}
// 初始化:从reducer收集状态
dispatch({type: ActionTypes.INIT})
const store {
...
}
return store
}
applyMiddleware
applyMiddleware是 Redux 里面最难理解的一部分了
enhancer
applyMiddleware实际上是一个 enhancer 工厂函数,先了解enhancer。
enhancer 是一个函数,故名思意是用来增强 createStore 的
大概长这样
function enhancer(createStore) {
return function myCreateStore(reducer, preloadedState) {
// 做些什么
const store = createStore()
return store
}
}
// 箭头函数的写法
const enhancer = createStore => (reducer, preloadedState) => {
// 做些什么
const store = createStore()
return store
}
applyMiddleware
好了,继续研究applyMiddleware
applyMiddleware是高阶函数,返回 enhancer
源码如下:
export default function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer<any> {
return createStore => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
// 4.0之前是 let dispatch = store.dispatch
let dispatch: Dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
// midelewareAPI 实际上是个mini store
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
// 包裹一层函数, 形成一个闭包
dispatch: (action, ...args) => dispatch(action, ...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 最终的dispatch
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
dispatch: (action, ...args) => dispatch(action, ...args),
这里用的非常巧妙,用闭包捕获 dispatch, 这样可以保证
所有 middleware 都能获取到最终的 dispatch。 有延迟的味道了,虽然传参数的时候 dispatch 就是个抛出异常的函数,但是真正要执行的时候,已经替换成最终的 dispatch了
为了方便理解可以隐藏细节,简化一下applyMiddleware
function applyMiddleware(middlewares) {
return function enhancer(createStore) {
return myCreateStore(reducer, preloadedState){
// 做些社么
}
}
}
function applyMiddleware(middlewares){
return (createStore)=>(reducer, preloadedState)=>{...}
}
什么是 Middleware
看一下(reducer, preloadedState)=>{...},这里处理传入的 middlewares
先看一下 Middleware 定义(简化)
interface Middleware<
S = any,
D extends Dispatch = Dispatch
> {
(api: MiddlewareAPI<D, S>): (
next: (action: unknown) => unknown
) => (action: unknown) => unknown
}
// 就是一个mini store
interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
dispatch: D
getState(): S
}
Middleware 接受一个 MiddlewareAPI(mini store), 返回一个函数, 这里命名为dispatchEnhancer。
dispatchEnhancer签名如下:
注意: dispatchEnhancer是我自己为了方便阅读增加的概念,源码里面没有这玩意
type DipatchEnhancer = (
next: (action: unknown) => unknown
) => (action: unknown) => unknown
// 为了更好理解, 可以简化成
type D = (action: unkion) => unknown
type DispatchEnhancer = (next: D) => D
dispatchEnhancer是一个高阶函数, 参数名为next的函数,然后返回一个函数:
很容易注意到:形参和返回值的类型都是(action: unknown)=> unknown
然后这个类型和最原始的dispatch类型是差不多的。得出以下结论:
- next 就是一个 dispatch 函数,可能是最原始,也可能是其它中间件
- 返回值也是个 dispatch 函数
- 这也是为什么我把这个函数命名为
dispatchEnhancer, 它和createStore的enhancer类似
处理传入的 middlewares
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
第 1 行把middlewareAPI传入所有 middleware 中,得到的 chain 就是一个DispatchEnhancer[],例如[loggerDisptchEnhancer, otherDispatchEnhancer ]
第 2 行先使用compose把所有dispatchEncher复合起来
const foo = compose(f, g, h)(...arg)
// 等价于
const foo = f(g(h(...arg)))
因此第 2 行可以理解为:
const dispatch = loggerDispatchEnhancer(otherDispatchEnhance(store.dispatch))
最终,我们在组件里调用的就是经过 middlewares 层层加工的dispatch。
看个例子
const logger_1: Middleware = store => {
return next => {
return function loggerDispatch_1(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
const logger_2: Middleware = store => {
return next => {
return function loggerDispatch_2(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
const store = createStore(
reducer,
{ value: 0 },
applyMiddleware(logger_1, logger_2)
)
store.dispatch({ type: 'ADD' })
当store.disaptch(...)的时候,执行栈如下
为什么这样设计?
下面都是自己的理解,可能有误
假设没有 redux 没有提供applyMiddleware。要实现类似的功能,可以写两个 enhancer:logger, otherEnhancer
const logger: StoreEnhancer = createStore => (reducer, state) => {
const store = createStore(reducer, state)
const dispatch = store.dispatch
const dispatchA: typeof dispatch = action => {
console.log('dispatchA', action)
return dispatch(action)
}
return {
...store,
dispatch: dispatchA
}
}
const otherEnhancer: StoreEnhancer = createStore => (reducer, state) => {
const store = createStore(reducer, state)
const dispatch = store.dispatch
const dispatchB: typeof dispatch = action => {
console.log('dispatchB', action)
return dispatch(action)
}
return {
...store,
dispatch: dispatchB
}
}
const store = createStore(reducer, { value: 0 }, compose(logger, otherEnhancer))
store.dispatch({ type: 'ADD' })
console.log(store.getState().value)
/* 输出如下
dispatchA {type: 'ADD'}
dispatchB {type: 'ADD'}
{value: 1}
*/
提炼出 myApplyMiddleware
这样完美实现了我们的要求,但是有很多重复的模板代码,显然可以提炼出myApplyMiddleware
function myApplyMiddleware(...middlewares: Function[]): StoreEnhancer {
return createStore => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
// 这里做些什么
return {
...store,
dispatch
}
}
}
const store = createStore(
reducer,
{ value: 0 },
myApplyMidlleware(loggerA, loggerB)
)
设计 middleware
现在的问题是 logger, otherMiddleware 怎么设计?我们把重复代码提炼成myApplyMiddleware, middleware 显然就是非公共部分了,
观察前面的enhancer:
const otherEnhancer: StoreEnhancer = createStore => (reducer, state) => {
const store = createStore(reducer, state)
const dispatch = store.dispatch
// =====start=====
const dispatchB: typeof dispatch = action => {
console.log('dispatchB', action)
return dispatch(action)
}
// =====end=====
return {
...store,
dispatch: dispatchB
}
}
根据上面的代码,我们马上想到middleware应该返回一个dispatch, 于是
function loggerMiddleware(store) {
return function dispatchA(action) {
console.log('dispatchA', action)
const result = store.dispatch(action)
return result
}
}
function otherMiddleware(store) {
return function dispatchB(action) {
console.log('dispatchB', action)
const result = store.dispatch(action)
return result
}
}
此时我们的myApplymiddleware
function myApplyMiddleware(...middlewares): StoreEnhancer {
return createStore => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
const chain = middlewares.map(middleware => middleware(store))
const dispatch = compose(...chain)
return {
...store,
dispatch
}
}
}
const store = createStore(
reducer,
{ value: 0 },
myApplyMidlleware(loggerMiddleware, otherMiddleware)
)
store.dispatch({ type: 'ADD' })
console.log(store.getState().value)
问题在哪里
一切看似完美,甚至觉得自己设计的 API 比原来的更优雅。然而运行结果显示我想多了:
dispatchB {type: 'ADD'}
dispatchA {type: 'ADD'}
{value: 2}
执行顺序和我们预料中的不一样就算了,结果也是错的。
- 顺序为什么是错的:
因为我们的 middleware 返回的是dispatch,即 dispatchA, dispatchB,
compose(dispathA, dispatchB)(action)
等价于
dispathc(dispatchB(action))
因此执行顺序错了
- 结果为什么是错的
看下我们的
middleware, 直接调用了store.dispatch
function otherMiddleware(store) {
return function dispatchB(action) {
console.log('dispatchB', action)
// =====start=====
const result = store.dispatch(action)
// =====end=======
return result
}
}
正确的做法是最原始的store.dispatch只会调用一次, 显然不能在中间件里面调用直接store.dispatch。
再观察一下前面写的enhancer.
const otherEnhancer: StoreEnhancer = createStore => (reducer, state) => {
const store = createStore(reducer, state)
const dispatch = store.dispatch
const dispatchB: typeof dispatch = action => {
console.log('dispatchB', action)
return dispatch(action)
}
return {
...store,
dispatch: dispatchB
}
}
// 等价于
const otherEnhancer: StoreEnhancer = createStore =>
function otherCreateStore(reducer, state) {
const store = createStore(reducer, state)
const dispatch = store.dispatch
const dispatchB: typeof dispatch = action => {
console.log('dispatchB', action)
return dispatch(action)
}
return {
...store,
dispatch: dispatchB
}
}
newCreateStore = compose(logger, otherEnhancer)(createStore)
// 等价于
newCreateStore = logger(otherEnhancer(createStore))
// 递归执行
newCreateStore(reducer, preloadedState)
-> otherCreateStore(...)
-> original createStore(...)
<- {...store, dispatch:dispatchA} <- {...store, dispatch:dispatchB} <- store
enhancer接受一个createStore返回一个新的createStore, 然后再 compose 了一组enhancer- 每个 enhancer 返回的
createStore, 通过递归调用
而我们middleware返回dispatch, 然后 compose 了一组dispatch
function loggerMiddleware(store) {
return function dispatchA(action) {
console.log('dispatchA', action)
const result = store.dispatch(action)
return result
}
}
function otherMiddleware(store) {
return function dispatchB(action) {
console.log('dispatchB', action)
const result = store.dispatch(action)
return result
}
}
// myApplyMiddleware
const chain = middlewares.map(middleware => middleware(store))
const dispatch = compose(...chain)
// main.js
const store = createStore(
reducer,
{ value: 0 },
myApplyMidlleware(loggerMiddleware, otherMiddleware)
)
store.dispatch({ type: 'ADD' })
console.log(store.getState().value)
// 期望的调用: 递归
dispatch(action):
dispatchA(action)
-> dispatchB(action)
-> original dispatch(action)
<- <- <-
// 实际的调用:线性
dispatch(action):
dispatchA(dispatchB(action))
dispatch(action):
dispatchB(action) -> original dispatch(action)
dispatchA(action) -> original dispatch(action)
重新设计 middleware
根据我们前面的观察,可以得出以下结论,
middleware不能直接返回dispatch, 因为它的类型是(action: T)=> T- 应该 compose
funtion(dispatch){return newDispatch},达到递归调用的目的 那么
function loggerMiddleware(store) {
return next =>
function dispatchA(action: any) {
console.log('dispatchA', action)
return next(action)
}
}
function otherMiddleware(store) {
return next =>
function dispatchB(action: any) {
console.log('dispatchB', action)
return next(action)
}
}
next就是dispatch
同时,myApplyMiddleware也要进行修改
function myApplyMiddleware(...middlewares: Function[]): StoreEnhancer {
return createStore => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
const chain = middlewares.map(middleware => middleware(store))
const dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
dispatchA {type: 'ADD'}
dispatchB {type: 'ADD'}
{value: 1}
总结
通常我们要递归调用都是要硬编码的:
function foo() {
console.log('foo enter')
bar()
console.log('foo exit')
}
function bar() {
console.log('bar enter')
baz()
console.log('bar exit')
}
function baz() {
console.log('baz enter')
console.log('baz exit')
}
但是如果这几个函数都不是我们能控制的,我们要怎么设计才能达到递归调用?
代码如下:
function fooWrapper(next) {
return function foo() {
console.log('foo enter')
next()
console.log('foo exit')
}
}
function barWrapper(next) {
return function bar() {
console.log('bar enter')
next()
console.log('bar exit')
}
}
function bazWrapper(next) {
return function baz() {
console.log('baz enter')
next()
console.log('baz exit')
}
}
compose(fooWrapper, barWrapper, bazWrapper)(() => {})()
// 更改调用顺序, 灵活
compose(barWrapper, bazWrapper, fooWrapper)(() => {})()
第一次阅读源码
收藏夹放了一堆经典必读源码,好几年也没动过,因为感觉很难读懂。上班了之后维护老项目,emmmm,也是源码吧。相对公司老项目代码, redux 源码看起来是那么赏心悦目、眉清目秀, 花了两天时间过了一下。
说实话有没有收获不知道,更多的是积累一些看源码的经验。
另外吐槽一下 yarn3, link 半天不行(也许是我太菜了),最后换了 pnpm link 上了