前置条件
背景
点击按钮,执行分析,打印结果。
1 | const add = (a, b) => a + b; |
新增需求 ,在分析时增加loading。
1 | const onClick = () => { |
这时候,loading和业务代码就已经耦合了。像loading、log等是最简单的耦合情况。
实际开发中会发生更复杂的耦合。比如在某个分析前后需要控制图层的加载。
1 | const onClick = () => { |
继续新增需求,在分析前要矫正参数,在分析后要过滤结果。
1 | const onClick = () => { |
这样整个函数就会越来越难以维护。
期望
loading函数应该和分析函数解耦,且形态大致如下:
1 | const loading = async (next) => { |
loading函数不关心分析函数的内容,它只关心执行的顺序,在分析前后显示loading。
onClick
函数也会对应修改为:
1 | const onClick = () => { |
联想
从loading函数的形态,很容易能联想到Redux或者Koa的中间件。
1 | const logger = store => next => action => { |
Koa:
1 | const logger = async (ctx, next) => { |
代码示例均来官方文档。
核心
因为redux的中间件必须和store绑定使用,不能直接使用,所以参考redux源码和[koa源码][https://github.com/koajs/compose/blob/master/index.js]实现了`applyMiddlewares`方法。
1 | const applyMiddlewares = |
函数的作用就是控制执行顺序。
输入是中间件函数middlewares
和上下文ctx
,输出是上下文ctx
。
中间件
现在把loading改造成为中间件。
1 | const middleware1 = async (ctx, next) => { |
把分析函数更新为异步函数,再套一层函数成为中间件。
1 | const add = (a, b) => { |
1 | const core = async (ctx, next) => { |
参数和结果都在ctx
中获取和赋值。
核心函数(最后一个函数)可不再调用next()
。
调用
调用时,传入中间件函数
1 | const ctx = await applyMiddlewares( |
控制台日志:
1 | middleware1 before at quokka.js:8:3 |
延伸
有了中间件之后,处理新的需求就变得非常简单。
middleware1作为loading函数,middleware2作为矫正参数,middleware3作为过滤结果即可。
像定位和高亮结果,也都可以作为中间件函数。
洋葱图:(矫正参数((((分析)过滤结果)定位)高亮))
代码:
1 | const ctx = await applyMiddlewares( |
执行顺序:矫正参数=》分析=》过滤结果=》定位=》高亮
附件
完整示例代码
1 | const add = (a, b) => { |