一、前言
最近在学习 redux,由于前面有过 Vuex 使用经验,所以觉得 redux 的使用方式何其也大致相同,结果...,是我想多了,这里不得不说对于我们这种后端思维来说,还是 Vue 比较容易接受一些,当然 react 也很优秀!好了,闲话不多说,接下来就进入正题。
二、redux 中的 middleWare
这里笔者也就过多花时间介绍 redux 了,我看的教程是是这个【Redux】,文中基础部分都讲得比较详细,看明白还是没有什么问题,但是其中高级部分讲解到的 redux middleware 概念的时候,其本身意义以及作用还是比较好理解!但是在文中详细的介绍了如何从一个简单的 middleware 实现到如果实现讲一个闭包 middleware 结构按照顺序安装到 redux 的时候,的确比较难理解,其中笔者觉得最难理解的当属文中提到的 applyMiddleware (一个“单纯”的为安装使用 middleware 的实现),代码如下:
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 })
}
middleware 的形式如下:
// 普通函数
function logger(store) {
return function wrapDispatchToAddLogging(next) {
return function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
// 使用 “箭头函数”替代,实现“柯里化 ”(读者自行了解)
// middleware 功能: 日志打印器
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
// middleware 功能: 崩溃信息收集
const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
// 如下代码需 raven-js 依赖, 这里为了演示效果注释掉.
/*
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
*/
throw err
}
}
使用方式如下:
/**
* 假设如下代码模拟 redux 的实际使用
*/
const ACTION_TYPES = {
NORMAL_ACTION : 'NORMAL',
EXCEPTION_ACTION : 'EXCEPTION'
}
const store = {
dispatch(action) {
const { NORMAL_ACTION, EXCEPTION_ACTION } = ACTION_TYPES;
if(action.type === NORMAL_ACTION) {
console.log('normal running...');
}
if(action.type === EXCEPTION_ACTION) {
console.log('exception running');
let i = 1 / 0; // 这里将会抛出异常
}
},
getState() {
return 'this is a foo state'
}
}
const storeEnforce = applyMiddleware(store, [logger, crashReporter]);
storeEnforce.dispatch({ type : ACTION_TYPES.NORMAL_ACTION});
storeEnforce.dispatch({ type : ACTION_TYPES.EXCEPTION_ACTION});
将如上的代码组合之后输出的结果如下:
normal running...
index4.html:52 next state this is a foo state
index4.html:50 dispatching {type: "EXCEPTION"}type: "EXCEPTION"__proto__: Object
index4.html:101 exception running
index4.html:52 next state this is a foo state
logger 和 crashReport 通过 applyMiddleware 几行精简的代码就被顺序的安装在在了 store.dispatch 执行之前,并且按照传入的顺序被顺序安装,那下面我们就来通过画图的方式理解 applyMiddleware 是如何实现上述的操作的。
上述的 applyMiddleware 可以中包含如下代码:
let dispatch = store.dispatch
middlewares.forEach(middleware =>
dispatch = middleware(store)(dispatch)
)
这段代码很不容易理解,我们接下来将其拆分成如下形式:
middlewares.reverse(); // 反转数组
let dispatch = store.dispatch
middlewares.forEach(middleware =>
// 第一次返回一个 function, 只要 tempFun 还能够被访问, 则作为参数传递进去的
// store 就不会被释放,这是 JS 中的闭包特性,读者可以自行了解 JS 闭包相关知识。
let tempFun = middleware(store)
// 由于 tempFun 依然是一个函数, 其接收一个“函数”,再返回一个包含“接收的参数(类型:函数)”的
// 函数(返回结果类型),又形成了一个闭包。
let previousCallback = tempFun(dispatch)
// 最后使 dispatch 指向新的 previousCallback, 二旧的 dispatch 已经保存在了
// previousCallback 这个闭包结构中
dispatch = previousCallback
)
通过上面的拆分可以中,middlewares 中的每个 middleware(嵌套闭包结构) 不断的被用来保存下一个 middleware 创造的闭包结构,因此在执行完成之后,其实循环完成之后 dispatch 是指向第一个 middleware 创造的闭包函数,然后 middleware 中接收的第二个参数 next 其实是指向下一个 middleware 创造的闭包函数,当然,最后一个 middleware 的 next 指向的是真实的dispatch, 这样,每个 middleware 中再调用 next, 相当于串联的在执行真实的 dispatch 之前把所有的 middleware 都调用了一遍,而我们只需要像平常一样调用 store.dispatch({...}) 就可以了,是不是很像“多米诺骨牌”。
是不是还有些不明白,接下来我们画图展示上述的过程:
好了,上图应该很清楚了,相信读者结合上图理解了 applyMiddleware 组装 middleware 原理之后,再去参看 react-redux 的 applyMiddleware 的源码将会很容易理解。
三、总结
通过 applyMiddleware 的学习,使我知道了 JS 的闭包还能有如此巧妙而且优雅的使用方式,同时也算学习了新知识,嗯,继续加油!!!