Skip to main content

Redux

以下源码参考自 Redux v5.0,为方便阅读去除了其中的 ts 代码

Redux 比较关键的几个点是:

  • 单向数据流
  • 单例模式,确保单个应用只有一个 store 实例
  • 发布订阅模式
  • 中间件、compose

下面从部分关键点的源码出发进一步理解 Redux 的实现,整体执行流程如下:

redux源码执行流程.png

createStore

创建一个 store 实例,接收三个参数

  • reducer:纯函数,接收上一个 state 和 action,根据不同 action 返回新的 state
  • preloadedState:设置 store 的默认值
  • enhancer:扩展 store 的功能
// 部分源码
createStore(reducer, preloadedState, enhancer) {
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error(
`Expected the enhancer to be a function. Instead, received: '${kindOf(
enhancer
)}'`
)
}
return enhancer(createStore)(
reducer,
preloadedState
)
}
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = [] // 存储更新回调
let nextListeners = currentListeners // 下次 dispatch 将触发的更新回调
let isDispatching = false // 防止 dispatch action 冲突

function getState() {
// ...
return currentState
}
function subscribe(listener) {
// ...
}
function dispatch(action) {
// ...
}
return {
dispatch,
subscribe,
getState,
}
}

getState

获取当前的 state

function getState() {
// ...
return currentState;
}

subscribe

可以存储一些更新回调函数,在 reducer 返回新状态的时候统一执行更新

  function subscribe(listener) {
let isSubscribed = true
ensureCanMutateNextListeners()
// 将更新函数推入到 listeners 数组,完成订阅
nextListeners.push(listener)

return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(...)
}

isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
// 取消订阅
nextListeners.splice(index, 1)
}
}

dispatch

通过派发一个 action 去通知相应的 reducer 更新 state,且只能通过这种方式修改,确保数据单向流动

function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(...)
}
if (typeof action.type === 'undefined') {
throw new Error(...)
}
if (isDispatching) {
throw new Error(...)
}

try {
// 上锁,如果已经在dispatch中,则不允许再度发起 dispatch
isDispatching = true
// 获取到当前的state
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}

const listeners = (currentListeners = nextListeners)
// 循环执行当前的 linstener
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}

combineReducers

合并多个 reducer,生成一个 rootReducer,通过它来统一处理 state

function combineReducers(reducers) {
...
let hasChanged = false
// 存放最终的所有的state
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)
// 以各自的reducerName为键,新生成的state作为值,生成最终的state object,
nextState[key] = nextStateForKey
// 判断所有的state变化没变化
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 变化了,返回新的state,否则,返回旧的state
return hasChanged ? nextState : state
}

中间件 middleware

一般 redux 工作流程是:dispatch 一个 action,rootReducer 找到指定 reducer,然后 reducer 根据 action 来执行相应的更新函数,更新 store。而 middleware 会转换 dispatch 函数,并用新的 dispatch 处理我们的 action,随后再交付 rootReducer 处理.

之前 createStore 函数里面有个 enhancer,其实就是在这里面执行中间件,最终返回一个改造后的 store

// createStore
...
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(
reducer,
preloadedState
)
}
...

applyMiddleware

applyMiddleware 的作用主要是接收中间件,改造 diapatch,返回一个 enhancer

function applyMiddleware(...middlewares) {
return (createStore) =>
(...args) => {
const store = createStore(...args);

// 传递到中间件的参数
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args),
};

const chain = middlewares.map((middleware) => middleware(middlewareAPI));

dispatch = compose(...chain)(store.dispatch);
// compose的作用类似于下面这样:
// compose(a,b,c) ==> (...args) => a(b(c(...args) ) )
return {
...store,
dispatch,
};
};
}
// compose
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args))
);
}

react-thunk

react-thunk 是 redux 中用于处理异步的中间件,本质上是改写了 dispatch 函数,使其可以接受一个函数作为参数。以 thunk 为例子,看下 redux 的中间件是怎么使用的。

先看下用法

import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(rootReducer, applyMiddleware(thunk))
// applyMiddleware(thunk)就是一个 enhancer

function callSbInOneSecond(name) {
return (dispatch, getState) => {
setTimeout({
dispatch({
type:'CALL_SOMEBODY',
payload: {
name
}
})
}, 1000)
}
}

store.dispatch(callSbInOneSecond('JacksonZhou'))

先掌握用法,再看源码,会更好理解 ~

function createThunkMiddleware(extraArgument) {
return function (_ref) {
// ①
var dispatch = _ref.dispatch,
getState = _ref.getState;

return function (next) {
// ②
return function (action) {
if (typeof action === "function") {
// 允许接收函数类型的action
return action(dispatch, getState, extraArgument);
}

return next(action);
};
};
};
}

var thunk = createThunkMiddleware;

结合上面的 createThunkMiddleware 和 applyMiddleware 再来走一遍 thunk

...
var thunk = createThunkMiddleware
createStore(rootReducer, applyMiddleware(thunk)) // applyMiddleware 接收中间件
...
return applyMiddleware(thunk)(createStore)( // 返回一个 enhancer
reducer,
preloadedState
)
...
// applyMiddleware(thunk)
const chain = middlewares.map(middleware => ①middleware(middlewareAPI))
dispatch = ②compose(...chain)(store.dispatch)
// next 其实就是 store.dispatch
return {
...store,
dispatch
}
// 此时已经返回一个修改了dispatch的store,new action都会走这 new dispatch