理解 Redux 的中间件
2019-10-08 23:54
841 查看
将该思想抽象出来,其实和 Redux 就无关了。问题变成,怎样实现在截获函数的执行,以在其执行前后添加自己的逻辑。 为了演示,我们准备如下的示例代码来模拟 Redux dispatch action 的场景: const store = { dispatch: action => { console.log("dispating action:", action); } }; store.dispatch({ type: "FOO" }); store.dispatch({ type: "BAR" }); 我们最终需要实现的效果是 Redux 中 applyMiddleware(...middlewares)的效果,接收一个中间件数据(函数数组),执行真正的 dispatch 前顺次执行这些中间件。 以打日志为例,我们想在调用 dispatch 时进行日志输出。 尝试1 - 手动直接的做法就是手动进行。 console.log("before dispatch `FOO`"); store.dispatch({ type: "FOO" }); console.log("before dispatch `FOO`"); console.log("before dispatch `BAR`"); store.dispatch({ type: "BAR" }); console.log("before dispatch `BAR`"); 但其实这并不算一个系统的解决方案,至少需要摆脱手动这种方式。 尝试2 - 包装既然所有 dispatch 操作都会打日志,完全有理由抽取一个方法,将 dispatch 进行包装,在这个方法里来做这些事情。 function dispatchWithLog(action) { console.log(`before dispatch ${action.type}`); store.dispatch(action); console.log(`after dispatch ${action.type}`); } 但调用的地方也得变,不能直接使用原始的 store.disatch而需要使用封装后的 dispatchWithLog: - store.dispatch({ type: "FOO" }); - store.dispatch({ type: "BAR" }); + dispatchWithLog({ type: "FOO" }); + dispatchWithLog({ type: "BAR" }); 尝试3 - 替换实现/Monkeypatching如果我们直接替换掉原始函数的实现,便可以做到调用的地方不受影响而实现新增的 log 功能,虽然修改别人提供的方法容易引起 bug 且不太科学。 const original = store.dispatch; store.dispatch = function log(action) { console.log(`before dispatch ${action.type}`); original(action); console.log(`after dispatch ${action.type}`); }; store.dispatch({ type: "FOO" }); store.dispatch({ type: "BAR" }); 尝试4 - 多个函数的截获除了添加 log,如果还想对每次 dispatch 进行错误监控,只需要拿到前面已经替换过实现的 dispatch 方法再次进行替换包装即可。 const original = store.dispatch; store.dispatch = function log(action) { console.log(`before dispatch ${action.type}`); original(action); console.log(`after dispatch ${action.type}`); }; const next = store.dispatch; store.dispatch = function report(action) { console.log("report middleware"); try { next(action); } catch (error) { console.log(`error while dispatching ${action.type}`); } }; 所以针对单个功能的中间件,我们可以提取出其大概的样子来了: function middleware(store) { const next = store.dispatch; store.dispatch = function(action) { // 中间件中其他逻辑 next(action); // 中间件中其他逻辑 }; } 改写日志和错误监控为如下: function log(store) { const next = store.dispatch; store.dispatch = function(action) { console.log(`before dispatch ${action.type}`); next(action); console.log(`after dispatch ${action.type}`); }; } function report(store) { const next = store.dispatch; store.dispatch = function(action) { console.log("report middleware"); try { next(action); } catch (error) { console.log(`error while dispatching ${action.type}`); } }; } 然后按需要应用上述中间件即可: log(store); report(store); 上面中间件的调用可专门编写一个方法来做: function applyMiddlewares(store, middlewares) { middlewares.forEach(middleware => middleware(store)); } 隐藏 Monkeypatching真实场景下,各中间件由三方编写,如果每个中间件都直接去篡改 store.dispatch不太科学也不安全。如此的话,中间件只需要关注新添加的逻辑,将新的 dispatch 返回即可,由框架层面拿到这些中间件后逐个调用并重写原来的 dispatch,将篡改的操作收敛。 所以中间件的模式更新成如下: function middleware(store) { const next = store.dispatch; - store.dispatch = function(action) { + return function(action) { // 中间件中其他逻辑 next(action); // 中间件中其他逻辑 }; } 改写 log和 report中间件: function log(store) { const next = store.dispatch; - store.dispatch = function(action) { + return function(action) { console.log(`before dispatch ${action.type}`); next(action); console.log(`after dispatch ${action.type}`); }; } function report(store) { const next = store.dispatch; - store.dispatch = function(action) { + return function(action) { console.log("report middleware"); try { next(action); } catch (error) { console.log(`error while dispatching ${action.type}`); } }; } 更新 applyMiddlewares方法: function applyMiddlewares(store, middlewares) { middlewares.forEach(middleware => { store.dispatch = middleware(store); }); } 最后,应用中间件: applyMiddlewares(store, [log, report]); 进一步优化之所以在应用中间件过程中每次都重新给 store.dispatch赋值,是想让后续中间件在通过 store.dispatch访问时,能够拿到前面中间件修改过的 dispatch函数。 如果中间件中不是直接从 store身上去获取 store.dispatch,而是前面已经执行过的中间件将新的 dispatch传递给中间件,则可以避免每次对 store.dispatch的赋值。 function applyMiddlewares(store, middlewares) { store.dispatch = middlewares.reduce( (next, middleware) => middleware(next), store.dispatch ); } 忽略掉实际源码中的一些差异,以上,大致就是 Redux 中间件的创建和应用了。 测试function m1(next) { return function(action) { console.log(`1 start`); next(action); console.log(`1 end`); }; } function m2(next) { return function(action) { console.log(`2 start`); next(action); console.log(`2 end`); }; } function m3(next) { return function(action) { console.log(`3 start`); next(action); console.log(`3 end`); }; applyMiddlewares(store, [m1, m2, m3]); store.dispatch({ type: "FOO" }); store.dispatch({ type: "BAR" }); } 输出结果: 3 start 2 start 1 start dispating action: { type: 'FOO' } 1 end 2 end 3 end 3 start 2 start 1 start dispating action: { type: 'BAR' } 1 end 2 end 3 end 相关资源 |
相关文章推荐
- redux深入理解之中间件(middleware)
- redux深入理解之中间件(middleware)
- redux深入理解之中间件(middleware)
- redux-applyMiddleware实现理解+自定义中间件
- redux深入理解之中间件(middleware)
- redux中间件之redux-thunk学习记录
- redux 理解
- React从入门到放弃(4):Redux中间件
- 第二章:理解面向消息的中间件和jms(1)
- express之中间件bodyParser的理解
- 我对消息中间件的理解
- JS 原型污染和猴子补丁 (装饰器)与redux中间件
- 一名iOSer对react-redux的理解
- 理解面向消息中间件及JMS 以及 ActiveMQ例子
- 浅谈对于react-thunk中间件的简单理解
- 我对消息中间件的理解
- 中间件通俗易懂的理解方式
- 深入理解React、Redux
- Redux的简单理解
- 深入理解Redux:10个来自专家的Redux实践建议