Redux是我们常用的一个状态管理库,经常会应用于结合react的项目开发中。最近研读了下Redux的源码,发现真的是非常的简短精炼啊,加上类型定义才一共一千多行的代码,相对于看庞大的react源码可以说是非常easy了。本着刨根问题的态度,对这个我经常性使用的库探究下源码实现。
(ps: Redux真的是深入贯彻了函数式编程理念) 我们从入口(index.ts)开始看:
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes, // 内置的一些actions
}
-
🌟createStore:顾名思义,创建一个store,并返回监听、派发store的一些方法。 -
🌟combineReducers: 将多个reducer合并为一个reducer -
bindActionCreators: 将action包裹一层dispatch方便直接调用 -
🌟applyMiddleware: 应用中间件 -
compose: 将多个函数组合成一个函数的工具方法(在applyMiddleware中使用)
让我们一个一个分析:
createStore
先来个例子看看Redux最基本的用法:
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
}
}
let store = createStore(counterReducer)
// 监听store数据变化
store.subscribe(() => console.log(store.getState()))
// 触发动作
store.dispatch({ type: 'counter/incremented' }) // {value: 1}
store.dispatch({ type: 'counter/incremented' }) // {value: 2}
store.dispatch({ type: 'counter/decremented' }) // {value: 1}
哎?看看这subscribe、dispatch。这不就是我们非常熟悉的订阅发布模式嘛!
让我们来看看简化版CreateStore源代码(一些很少用的方法先不分析):
export default function createStore(reducer, preloadedState?, enhancer?){
// 这个enhancer待会儿讲...
if (typeof enhancer !== 'undefined') {
return enhancer(createStore)(
reducer,
preloadedState
)
}
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
// 返回当前state
function getState() {
return currentState
}
// 添加监听者
function subscribe(listener) {
nextListeners.push(listener)
// 返回卸载监听者方法
return function unsubscribe() {
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
// 派发动作
function dispatch(action) {
// 获取新的state
currentState = currentReducer(currentState, action)
// 执行所有的监听者方法
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
// 创建完store后会派发默认的初始化动作,初始化状态
dispatch({ type: ActionTypes.INIT } as A)
const store = {
dispatch: dispatch,
subscribe,
getState,
}
return store
}
可以看到正如我们所想:通过subscribe方法我们可以注册监听者。当触发dispatch方法时,我们先根据action调用reducer来变更state,然后再通知到所有的监听者。
Redux的基本工作原理应该比较清晰了,那我们抛出几个问题:
-
为什么要叫
reducer? -
enhancer是干嘛的? -
中间件是怎么应用的?
-
看
createStore参数只有一个reducer,一个reducer处理太多state的话太庞杂了,有没有什么办法能分割?
首先第一个问题:为什么要叫reducer?
是不是跟我们常用的js中Array的reduce方法有点眼熟。官方说:
“ A function that returns the next state tree, given the current state tree and the action to handle.”
然后我们看看Array.reduce一般是咋用的:
[1,2,3,4,5].reduce((acc, cur) => acc + cur) // 15
再看看reducer函数:
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
}
}
我们类比一下:其实reducer可以看作是函数式编程中演绎高阶函数:reduce,中的那个函数参数。 acc等价于state,cur等价于action,函数返回操作后的新的state。因此叫做reducer。
第二个问题:enhancer是干嘛的?
createStore中有一个参数enhancer(增强器),顾名思义用于增强redux的能力。其本质是一个高阶函数,它的参数是redux的createStore函数,返回一个功能更强大的enhanced createStore。
一般我们是这么使用的:
function enhancerCreator() {
return createStore => (reducer, initialState, enhancer) => {
// 先执行旧的createStore
let store = createStore(reducer, initialState, enhancer);
// 对旧的store做一些处理,并返回新的store。最常见的是处理dispatch函数,加一些劫持处理
function dispatch(...args){
console.log(args);
return store.dispatch(...args);
};
return {...store, dispatch}
}
}
const store = createStore(
reducer,
enhancerCreator()
);
看到这我们可能会有一个问题,因为我们都知道redux中间件其实就是劫持dispatch过程,并加入一些操作。那这个enhance和applyMiddleware有啥关系?
其实applyMiddleware就是一个enhancerCreator
const store = createStore(
reducer,
applyMiddleware([...middlewares])
);
applyMiddleware
Creates a store enhancer that applies middleware to the dispatch method of the Redux store. This is handy for a variety of tasks, such as expressing asynchronous actions in a concise manner, or logging every action payload.
那其实applyMiddleware的代码就比较清晰了:
export default function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer<any> {
return (createStore) => (reducer, preloadedState?) => {
const store = createStore(reducer, preloadedState)
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
}
}
}
重点在于标记部分代码。
我们知道一个中间件基本上是下面这个样子:
/**
* 我们知道 type Dispatch<T> = (action: T, ...extra) => T
* 那下面中间件类型可以简化为:(api):(next: Dispatch) => Dispatch
*/
const middleware = (api) => (next: Dispatch) => (action) => {
// do something
}
标记部分的chain = ...那一行其实是给每一个中间件(高阶函数)赋api的值。赋值后chain中的每一个函数类型都是type ChainItem = (next: Dispatch) => Dispatch。是不是很眼熟,传入一个dispatch,返回一个新的dispatch,跟上文的enhance是很类似的模式。这也是我们非常常见的中间件模式(管道模式的一种实现),典型的洋葱模型。
第二行的dispatch = ...其实就是利用compose函数将这些方法组合起来
function compose(...funcs: Function[]) {
return funcs.reduce((a, b) => (...args: any) => a(b(...args))
}
combineReducers
Turns an object whose values are different reducer functions, into a single reducer function. It will call every child reducer, and gather their results into a single state object, whose keys correspond to the keys of the passed reducer functions.
combineReducers其实就是将多个reducer组合成一个,这样用户编程时就可以拆分成多个reducer处理。
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(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]
}
}
const finalReducerKeys = Object.keys(finalReducers)
// 整合后的reducer
return function combination(
state = {},
action: AnyAction
) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
}
}
我们使用的时候,看起来就像分割成了多个reducer,但是本质上还是一个~
const store = createStore(
combineReducers({
reducer1: reducer1,
reducer2: reducer2,
}),
);
后记
单看redux源码的话是比较简单的,实现的很精炼,其中对函数式编程的应用也很透彻。尤其其中的监听者模式的实现以及中间件模式的实现,还是很值得学习的。
不过redux只是一个基石,在redux之上我们还有很多工具:比如提供了更便捷api和很多的中间件的redux-tookit,再比如和react结合使用的react-redux,也是值得一探,就留给大家自己探索啦。