Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。比如日志中间件logger可以在你每次dispatch派发动作的时候打印新旧的状态。
通常要实现这一的功能我们也可以在代码中调用dispatch的地方手动记录状态即可,而没必要使用中间件。
let action = addTodo('Use Redux')
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
虽然这样做达到了想要的效果,但是你并不想每次都这么干。
于是你将上面的操作抽取成一个函数:
function logger(store, action) {
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
}
然后用它替换 store.dispatch():
logger(store, addTodo('Use Redux'))
但是每次都要导入一个外部方法总归还是不太方便。
于是你想到重写dispatch函数,直接替换 store 实例中的 dispatch 函数
let next = store.dispatch
store.dispatch = function logger(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
无论我们在哪里发起 action,保证都会被记录。如果有多个中间件,你也可以封装成函数,然后分别调用
function logger(store) {
let next = store.dispatch
store.dispatch = function (action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
function report(store) {
let next = store.dispatch
store.dispatch = function (action) {
try {
return next(action)
} catch (err) {
console.error('捕获一个异常!', err)
throw err
}
}
}
logger(store)
report(store)
在之前,我们用自己的函数替换掉了 store.dispatch。如果我们不这样做,而是在函数中返回新的 dispatch 呢?
function logger(store) {
let next = store.dispatch
// 我们之前的做法:
// store.dispatch = function (action) {
return function (action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
function report(store) {
let next = store.dispatch
return function (action) {
try {
return next(action)
} catch (err) {
console.error('捕获一个异常!', err)
throw err
}
}
}
function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
// 在每一个 middleware 中变换 dispatch 方法。
middlewares.forEach(middleware =>
store.dispatch = middleware(store)
)
}
applyMiddleware(store, [ logger, report ])
以让 middleware 以方法参数的形式接收一个 next() 方法,而不是通过 store 的实例去获取。
function logger(store) {
// 我们之前的做法:
// let next = store.dispatch
return function (next) {
return function (action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
再次修改applyMiddleware函数
function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
let dispatch = store.dispatch
middlewares.forEach(middleware =>
dispatch = middleware(store)(dispatch)
)
}
最后源码
function applyMiddleWare(...middlewares) {
return function (createStore) {
return function (reducer) {
let store = createStore(reducer)
let dispatch = () => {
throw new Error('现在还不能用')
}
let middleAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middleAPI))
function compose (...funcs) {
if (funcs.length === 0) {
return args => args
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
}
注意compose如何执行中间件的,你就明白为什么叫next,因为next指向下一个中间件函数
function fn1(p) {
console.log(p, 'fn1')
}
function fn2(p) {
console.log(p, 'fn2')
}
function compose (arr) {
return arr.reduce((a, b) => (...args) => a(b(...args)))
}
let dispatch = compose([fn1, fn2])
console.log(fn(0))
// 0 fn2
// 10 fn1
// 30
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!