想着对 React 生态更深刻的学习,何况 React 源码内容也很少的状况,来读一下他的源码内容。 目前 Redux 已是 TypeScript 版本的源码,我正在阅读的则是
4.0.4
版本代码。typescript
很显然,能够看到这和 React 仓库同样,一样使用了 rollup 打包工具,也验证了 rollup 在开发库类仓库的优点,毕竟大厂都在用嘛。json
咱们先来看到 package.json
:redux
{ "name": "redux", "version": "4.0.4", "main": "lib/redux.js", "unpkg": "dist/redux.js", "module": "es/redux.js", "types": "types/index.d.ts", } 复制代码
我省略了大部份内容,能够看到开发团队针对不一样使用场景,对外提供包对应的路径,好比使用 TypeScript 时,对应的则是 types/index.d.ts
,或者使用 ES6 模块化引入时则是 es/redux.js
。值得咱们学习。api
接下来,咱们看到源码部分,整个源码学习内容,将围绕着 src
进行, utils
下的内容即一些简单的工具, types
下则是一些类型的定义。数组
示例代码中我会移除并省略掉一些逻辑代码和 TypeScript 相关的函数重载代码,以及一些我认为不那么重要的代码,即只展示我想要说明的内容。markdown
那么咱们先来看到 createStore
函数吧。app
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 { // 接受的 reducer let currentReducer = reducer // 预加载 state let currentState = preloadedState as S // 订阅队列 let currentListeners: (() => void)[] | null = [] // 下一个订阅队列,看起来更像是订阅队列的副本,下面我会来介绍他和订阅队列的不解之谜 let nextListeners = currentListeners // 检查并建立这个副本的函数 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { // 拷贝到一个新的数组引用,能够理解为 [...currentListeners] 使得 nextListeners !== currentListeners nextListeners = currentListeners.slice() } } // 得到 state function getState(): S { return currentState as S } // 订阅函数 function subscribe(listener: () => void) { ensureCanMutateNextListeners() // 操做订阅队列副本,与 currentListeners 之间不受影响 nextListeners.push(listener) // 返回取消订阅函数 return function unsubscribe() { ensureCanMutateNextListeners() // 一样的效果 const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } // 看到这里你是否是会想,建立这个副本的目的是什么? // 官方给到的解释:This prevents any bugs around consumers calling subscribe/unsubscribe in the middle of a dispatch. } // dispatch 函数 function dispatch(action: A) { currentState = currentReducer(currentState, action) // 遍历通知订阅队列,将 currentListeners 赋值为“最新”的队列 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } // 替换 reducer 函数 function replaceReducer<NewState, NewActions extends A>( nextReducer: Reducer<NewState, NewActions> ): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext { // 更新 reducer ;((currentReducer as unknown) as Reducer< NewState, NewActions >) = nextReducer // 同初始化功能一致,即生成 currentState ,下面代码中会说起 dispatch({ type: ActionTypes.REPLACE } as A) return (store as unknown) as Store< ExtendState<NewState, StateExt>, NewActions, StateExt, Ext > & Ext } // 初始化,生成 currentState ,即调用 getState 函数返回的内容, ActionTypes.INIT 便可以理解为私有类型,不会影响到你本身写的 reducer 毕竟里面包含了随机数... dispatch({ type: ActionTypes.INIT } as A) // 暴露的 store 对象 const store = ({ dispatch: dispatch as Dispatch<A>, subscribe, getState, replaceReducer, // 观察者方法这里不作解读,能够理解为对 subscribe 的封装 [?observable]: observable } as unknown) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext return store } 复制代码
那么到此,咱们看到了整个 createStore
函数的实现过程以及返回的内容。模块化
这里我来简单的代过 compose
函数,也是业界的经常使用实现,不是很明白其功能的同窗能够简单看到这段代码便可:函数
(funcs as : Function[]).reduce((a, b) => (...args: any) => a(b(...args))) 复制代码
接下来,咱们来看看 combineReducers
的实现,你能够把这个函数理解为对 reducer
的合并,固然函数名就是这个意思。工具
export default function combineReducers(reducers: ReducersMapObject) { const reducerKeys = Object.keys(reducers) // 用于 combination 函数中使用所需,收集的有效 reducers const finalReducers: ReducersMapObject = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } // 收集的 keys const finalReducerKeys = Object.keys(finalReducers) return function combination( state: StateFromReducersMapObject<typeof reducers> = {}, action: AnyAction ) { let hasChanged = false // 新的 state const nextState: StateFromReducersMapObject<typeof reducers> = {} // 遍历 keys for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] // 从遍历中更新 state const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length // 返回 state 或新的 state return hasChanged ? nextState : state } } 复制代码
在咱们看完 reducer
的组合工具函数后,咱们来看到绑定执行上下文的语法糖函数 bindActionCreators
。
实现代码不多,即绑定执行上下文环境 this
。具体使用场景,能够看到官网 API 文档 bindActionCreators(actionCreators, dispatch) ,那么直接看到源码:
function bindActionCreator<A extends AnyAction = AnyAction>( actionCreator: ActionCreator<A>, dispatch: Dispatch ) { return function(this: any, ...args: any[]) { return dispatch(actionCreator.apply(this, args)) } } export default function bindActionCreators( actionCreators: ActionCreator<any> | ActionCreatorsMapObject, dispatch: Dispatch ) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } const boundActionCreators: ActionCreatorsMapObject = {} for (const key in actionCreators) { const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators } 复制代码
最后,咱们来看到中间件函数 applyMiddleware
。
export default function applyMiddleware( ...middlewares: Middleware[] ): StoreEnhancer<any> { return (createStore: StoreCreator) => <S, A extends AnyAction>( reducer: Reducer<S, A>, ...args: any[] ) => { const store = createStore(reducer, ...args) let dispatch: Dispatch = () => { throw new Error('...') } const middlewareAPI: MiddlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 对 store.dispatch 进行改写,将其包装在中间件的最后一层,以实现中间件的功能 dispatch = compose<typeof dispatch>(...chain)(store.dispatch) return { ...store, dispatch } } } 复制代码
OK, Redux 源码学习完了,是否是没看够,确实!
这才是精妙的库设计,简洁的代码实现强大的功能,那么此次没过瘾不要紧,下次咱们来品一品 Redux 是如何和 React 结合在一块儿的吧。