序言
前段时间,大概看了下redux原理,主要是想了解下这个库是怎么处理状态变更的,但是也只是看了个大概。最近,想要深入的理解一下底层的原理,知其然知其所以然,便写了个demo,深入了解了下。
redux项目
最新版本的redux源码已经用typescript重写,还是比较易于阅读的。
redux仓库地址: github.com/reduxjs/red…
如何学习源码
- 新建demo仓库,引入redux源码,断点调试,查看整个流程
- 查找网上的redux源码分析文章,结合别人的分析加上自己的理解去学习
demo
- 为了快速搭建项目,使用create-react-app脚手架
npx create-react-app redux-demo --typescript
- 其他具体细节,可以拉该仓库下来去了解: redux-demo
源码分析
-
新建reducer
- 创建testInfo-reducer
const INITIAL_STATE = { display: true }; export function testInfo(state = INITIAL_STATE, action: any) { switch (action.type) { case 'update': state = { display: action.data }; break; } return state; } - 创建reducers文件
import { combineReducers } from 'redux' import { testInfo } from './testInfo'; export default combineReducers({ testInfo })combineReducers做了什么?
function assertReducerShape(reducers: ReducersMapObject) { Object.keys(reducers).forEach(key => { const reducer = reducers[key] const initialState = reducer(undefined, { type: ActionTypes.INIT }) if (typeof initialState === 'undefined') { throw new Error( `The slice reducer for key "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined. If you don't want to set a value for this reducer, ` + `you can use null instead of undefined.` ) } if ( typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() }) === 'undefined' ) { throw new Error( `The slice reducer for key "${key}" returned undefined when probed with a random type. ` + `Don't try to handle '${ActionTypes.INIT}' or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined, but can be null.` ) } }) } export default function combineReducers(reducers: ReducersMapObject) { // 获取传入 reducers 对象集合的key值,此处我们传入的对象是 {'testInfo': testInfo}, // 因此得到的 reducerKeys = ['textInfo']; const reducerKeys = Object.keys(reducers) // 由于传入的reducer必须是Function,因此需要做一层过滤 const finalReducers: ReducersMapObject = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] // 判断当前的reducer是否是function,是的话,存入finalReducers的缓对象集合中 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } // 最终得到的 finalReducerKeys = {'testInfo': testInfo} // 获取最终生成的reducer的key值集合,此处是['testInfo'] const finalReducerKeys = Object.keys(finalReducers) let shapeAssertionError: Error try { // 此处可以理解为对reducer做了一层校验,确保每个reducer的初始值均不是undefined assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e as Error } // 最终生成一个combination函数,入参是state & action return function combination(state: StateFromReducersMapObject<typeof reducers> = {},action: AnyAction) { if (shapeAssertionError) { throw shapeAssertionError } // 标记执行完所有的reducer之后,状态有没有发生变化,如果有,需要返回变更之后新的state,否则返回旧的state即可 let hasChanged = false const nextState: StateFromReducersMapObject<typeof reducers> = {} // 根据reducer的key值,来依次执行所有的reducer for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] // 取到当前需要执行的reducer函数 const reducer = finalReducers[key] // 获取当前reducer变更之前的值 const previousStateForKey = state[key] // 执行reducer函数,入参是之前的值,以及当前可能需要发生变化的行为,获取reducer执行之后的值 const nextStateForKey = reducer(previousStateForKey, action) // 如果执行之后的值是undefined,说明传入的action提供的值异常 if (typeof nextStateForKey === 'undefined') { const actionType = action && action.type throw new Error( `When called with an action of type ${actionType ? `"${String(actionType)}"` : '(unknown type)' }, the slice reducer for key "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.` ) } // 添加新的状态值到nextState中 nextState[key] = nextStateForKey // 比较当前值与之前的值是否相等,不相等,则标记hasChanged发生改变 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 执行完所有的reducer之后,判断下是否发生变化 hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length; // 发生变化,返回新的状态对象,否则返回旧值 return hasChanged ? nextState : state } }
- 创建testInfo-reducer
-
创建store
-
创建store文件
import { createStore, applyMiddleware, compose } from 'redux'; import rootReducer from './reducers' // 为了说明中间件的注册流程,引入redux-logger插件 import { createLogger } from 'redux-logger'; const middlewares: never[] = [] if (process.env.NODE_ENV === 'development' && process.env.TARO_ENV !== 'quickapp') { // 开发环境注入 middlewares.push(createLogger() as unknown as never) } // 生成中间件function集合 const middlewareFuncs = applyMiddleware(middlewares); // 包装生成最终的function const enhancer = compose(middlewareFuncs); // 创建store const store = createStore(rootReducer, enhancer); export { store };- applyMiddleware生成了什么?
// 基础action,仅包含操作的type export interface Action<T = any> { type: T } // 扩充的action,包含除了type之外的扩充字段,一般习惯用action = {type: 'xxx', data: {}} export interface AnyAction extends Action { [extraProps: string]: any } // dispatch函数声明可以理解为type dispatch = (action: AnyAction, ...extraArgs:any[])=>AnyAction; export interface Dispatch<A extends Action = AnyAction> { <T extends A>(action: T, ...extraArgs: any[]): T } // 中间件API类型,必须包含dispatch & getState方法 export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> { dispatch: D getState(): S } // 中间件类型,约定该函数是(api: MiddlewareAPI): ((next: D) => (action: D extends Dispatch<infer A> ? A : never) => any) export interface Middleware<_DispatchExt = {}, S = any, D extends Dispatch = Dispatch> { (api: MiddlewareAPI<D, S>): (next: D) => (action: D extends Dispatch<infer A> ? A : never) => any } // 接收的参数是一个 export default function applyMiddleware(middlewares: Middleware[]): StoreEnhancer { // 执行完applyMiddleware之后,实际上返回了一个function,该函数的入参是createStore【详见store定义】 // 返回值是入参 (reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>),返回值是{...store和合成之后的dispatch} return (createStore: StoreEnhancerStoreCreator) => <S, A extends AnyAction>(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>) => { // 创建store const store = createStore(reducer, preloadedState) // 定义dispatch 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) } // 依次执行注入的中间件数组,生成新的 (next: D) => (action: D extends Dispatch<infer A> ? A : never) => any 函数结合 const chain = middlewares.map(middleware => middleware(middlewareAPI)); // 生成新的dispath,这些dispatch已经包裹了所有的中间件,生成了新的dispatch dispatch = compose<typeof dispatch>(...chain)(store.dispatch) // 生成新的store return { ...store, dispatch } } }- compose生成了什么?
type Func<T> = (...args: any[]) => T; // 入参是一个函数数组集合 export default function compose<T>(...funcs: Func<T>[]): Func<T> { if (funcs.length === 0) { return (arg: T) => arg } if (funcs.length === 1) { return funcs[0] } // 不做解释,自己看下reduce return funcs.reduce((a, b) => (...args: any) => a(b(...args))) }- createStore生成了什么?
// reducer是combineReducers之后生成的(state: any, action:any)=> state:any // PreloadedState为可选参数,是一个对象,为state的初始状态,一般不传 // enhancer 一般为中间件compose之后的function: createStore => (reducer, preloadedState) => {...} export default function createStore<State, A extends Action, Ext = {}, StateExt = never>(reducer: Reducer<State, A>, preloadedState?: PreloadedState<State> | StoreEnhancer<Ext, StateExt>, enhancer?: StoreEnhancer<Ext, StateExt>): Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext { if ((typeof preloadedState === 'function' && typeof enhancer === 'function') || (typeof enhancer === 'function' && typeof arguments[3] === 'function')) { throw new Error( 'It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.' ) } if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState as StoreEnhancer<Ext, StateExt> preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error( `Expected the enhancer to be a function. Instead, received: '${kindOf( enhancer )}'` ) } // 外部调用完一下代码之后,创建createStore的流程 // const middlewareFuncs = applyMiddleware(middlewares); // const enhancer = compose(middlewareFuncs); // const store = createStore(rootReducer, enhancer); // enhancer是中间件合成之后的function,入参为createStore本身,执行之后,返回:(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>)=> store // 再次执行(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>)=> store,返回创建的store,此时的store.dispatch已经被中间件包裹,生成新的function return enhancer(createStore)(reducer, preloadedState as PreloadedState<State>) as Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext } if (typeof reducer !== 'function') { throw new Error( `Expected the root reducer to be a function. Instead, received: '${kindOf( reducer )}'` ) } // return enhancer(createStore)(reducer, preloadedState as PreloadedState<State>) as Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext这个function调用里,会调用createStore方法,详见applyMiddleware实现 // 初始化当前的reducer【合成之后】 let currentReducer = reducer // 初始化当前状态,默认是undefined let currentState = preloadedState as State // 初始化当前监听者【主要用于store里边的state发生变化时,通知相关的监听者】 let currentListeners: (() => void)[] | null = [] let nextListeners = currentListeners let isDispatching = false function ensureCanMutateNextListeners() { // 如果当前的nextListeners & currentListeners指向的是同一个引用,则生成新的拷贝,并赋值给nextListeners if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } function getState(): State { 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.' ) } return currentState as State } function subscribe(listener: () => void) { if (typeof listener !== 'function') { throw new Error( `Expected the listener to be a function. Instead, received: '${kindOf( listener )}'` ) } 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/store#subscribelistener for more details.' ) } let isSubscribed = true // 生成新的nextListeners ensureCanMutateNextListeners() // 此时nextListeners的变更不影响currentListeners nextListeners.push(listener) // 返回值是一个取消订阅的api return function unsubscribe() { if (!isSubscribed) { return } if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.' ) } isSubscribed = false // 生成新的nextListeners,nextListeners的变化不影响currentListeners ensureCanMutateNextListeners() // 找到需要移除的listener,并移除 const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) // 重置当前的currentListeners集合 currentListeners = null } } function dispatch(action: A) { // 要求传入的action必须是一个字面量对象 if (!isPlainObject(action)) { throw new Error( `Actions must be plain objects. Instead, the actual type was: '${kindOf( action )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.` ) } // 要求传入的action必须有type字段,且不为undefined 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.' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true // 调用所有的reducer生成新的state对象 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 } function replaceReducer<NewState, NewActions extends A>(nextReducer: Reducer<NewState, NewActions>): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext { if (typeof nextReducer !== 'function') { throw new Error( `Expected the nextReducer to be a function. Instead, received: '${kindOf( nextReducer )}` ) } (currentReducer as unknown as Reducer<NewState, NewActions>) = nextReducer dispatch({ type: ActionTypes.REPLACE } as A) return store as unknown as Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext } // 一般用不到 function observable() { const outerSubscribe = subscribe return { subscribe(observer: unknown) { if (typeof observer !== 'object' || observer === null) { throw new TypeError( `Expected the observer to be an object. Instead, received: '${kindOf( observer )}'` ) } function observeState() { const observerAsObserver = observer as Observer<State> if (observerAsObserver.next) { observerAsObserver.next(getState()) } } observeState() const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } } // 创建的时候,做一次初始化操作 dispatch({ type: ActionTypes.INIT } as A) const store = { dispatch: dispatch as Dispatch<A>, subscribe, getState, replaceReducer, [$$observable]: observable } as unknown as Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext return store }
-
-
使用store,并触发变更
-
新建storeTest文件,并添加store的事件监听
``` import { store } from './store'; const listener1 = store.subscribe(() => { console.error('listener1 getState:', store.getState()); }) const listener2 = store.subscribe(() => { console.error('listener2 getState:', store.getState()); }) const listener3 = store.subscribe(() => { console.error('listener3 getState:', store.getState()); }) ``` -
触发store变更
setInterval(() => { store.dispatch({ type: 'update', data: Date.now() }) })
-
总结
- redux核心代码还是比较少的,作者非常的巧妙的运用了闭包和函数柯里化。初读确实会有些晦涩难懂,再看不禁要发出赞叹,写的太好了。