redux中间件函数
middleware中间件函数
中间件本质就是得到一个新的dispatch创建函数,调用创建函数,重写了store.dispatch(action),依次执行新的dispatch(action)方法,来改变store中的state
我们从一个小案例来说明中间件函数的由来:
我们需要记录一下操作类型action和下一个状态state
//由action创建函数addTodo创建一个action对象
const action = addTodo('Use Redux')
//打印action对象
console.log('dispatching', action)
store.dispatch(action)
//打印下一个状态
console.log('next state', store.getState())
这是我们想要的效果,但不想每次都这样做手动创建,我们希望用一个函数来自动创建
封装dispatch函数
将日志记录提取到函数中:
function dispatch(store, action) {
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
}
然后,在任何地方使用它,而不是store.dispatch():
dispatch(store, addTodo('Use Redux'))
我们可以到这里结束,但是每次都导入一个特殊的函数不是很方便。
使用 Monkeypatching (猴子补丁)重写dispatch方法
Monkeypatching 是一种黑客攻击。“替换任何你喜欢的方法”
const next = store.dispatch //保存源dispatch
//重写dispatch
store.dispatch = function dispatch(action) {
console.log('dispatching', action)
let result = next(action) //调用原始的dispatch方法
console.log('next state', store.getState())
return result
}
同样,我们我们也使用一个函数封装一下,这样我们就不用每次都是编写一遍,这样就是一个中间件雏形了
function logMiddileware(store) {
const next = store.dispatch //保存源dispatch
//重写dispatch
store.dispatch = function dispatch(action) {
console.log('dispatching', action)
//调用原始的dispatch方法,相当于store.dispatch(action)
//store.dispatch(action)返回结果是当前action对象
let result = next(action)
console.log('next state', store.getState())
return result
}
}
这已经更接近我们想要的了!无论我们在哪里调度一个动作,它都保证被记录。但总感觉哪里不对
使用多个 Monkeypatching (猴子补丁)重写dispatch方法
增加一个错误报告
function logMiddileware(store) {
const next = store.dispatch
store.dispatch = function dispatch(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
function errorMiddileware(store) {
const next = store.dispatch
store.dispatch = function dispatch(action) {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
}
logMiddileware(store)
errorMiddileware(store)
不过,这并不好,感觉怪怪的,我们使用dispatch传递的参数是action,这里传递的是store,就让人有些疑惑,所以我们希望返回一个封装后的dispatch函数。
function logMiddileware(store) {
const next = store.dispatch
// 之前是直接将store.dispatch重写
// store.dispatch = function dispatch(action) {}
return function (action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
let dispatch=store.dispatch(store)
dispatch(action)
但是我们调用logger函数是还是要传递store对象,不是我们想要的,所以我们使用一个辅助函数来实现我们想要的store.dispatch(action)
//我们在这里传递store
function applyMiddlewareByMonkeypatching(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
//在这里重写了store.dispatch
middlewares.forEach(middleware => (store.dispatch = middleware(store)))
}
这样我们就可以直接调用store.dispatch(action)
但是
middlewares.forEach(middleware => (store.dispatch = middleware(store)))
store.dispatch(action)还是被重写了,
为什么我们还是要在applyMiddlewareByMonkeypatching中重写store.dispatch,这样目的是使每个中间件都能调用store.dispatch,这样中间件都被链接起来了,但是,它仍然是猴子补丁,我们要想办法解决。
修改中间件
function logMiddileware(store) {
//返回一个dispatch创建函数,这样我们就可以接收store.dispatch当做参数
return function dispatchCreator(dispatch) {
return function dispatch(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
使用箭头函数更加精简
const logMiddileware = store => dispatch => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
我们通常会把dispatch函数用next来命名
const logMiddileware = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
修改applyMiddlewareByMonkeypatching辅助函数
我们把applyMiddlewareByMonkeypatching辅助函数修改一下,变成下面这个样子:
function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
let dispatch = store.dispatch
middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)))
return Object.assign({}, store, { dispatch })
}
注意这里,我们先声明了变量dispatch,然后将store.dispatch赋值给他,这样dispatch 是当做参数被传递进去,没有被重写
let dispatch = store.dispatch
middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)))
- 第一次传递的dispatch是原始的store.dispatch
- middleware(store)返回dispatch创建函数dispatchCreator,再调用创建函数dispatchCreator(dispatch),然后在返回dispatch函数,每次循环的时候他会把上一个dispatch函数传给下一个中间件函数,依次从外到内传递下去,直到最后一个。
- 最后一个中间件运行的会调用传递的dispatch(action),然后又依次从内到外运行dispatch(action)