今天就谈谈koa中的中间件
- 人就是这样,在使用一样东西的时候,可能更多的是关注这件东西怎么去使用,或者说怎么才可以才更好的使用,但是往往这样就会忽略这件东西的源代码的实现,或者说核心原理的实现
- 我在使用koa框架的时候,就是为了创建一个简单的http服务器来运行项目就可以了,没想别的,也没怎么深入的去理解koa中间件的原理实现,不过有一点可以确定,那就是既然koa是用javascript写的,那么,使用 javascirpt就是可以原生的写出来,没有做不到,只有想不到
- 今天就来谈谈 koa中间件是怎么写出来的,或者说根据需求,写的思路是怎样的
先看看需求
- 任何一个函数的出现或者诞生,都是有需求,才会诞生,没有需求的函数写出来是没有意义的
app.use(function(next){
console.log(1)
next()
console.log(2)
})
app.use(function(next){
console.log(3)
next()
console.log(4)
})
app.use(function(next){
console.log(5)
next()
console.log(6)
})
app.compose()
- 就像上面的例子,我有这么一个需求,中间件是通过 use这个函数来注册的,或者说通过 use这个函数来添加的,然后,通过 compose这个函数来使用中间件,或者说启动中间件,但是呢,这里有一个亮点,就是中间件的参数 next是一个函数,一旦调用这个 next函数,那么就会 启动下一个中间件,就是这么一个需求
- 可以想到,根据需求上面里子的输出结果应该是 1,3,5,6,4,2
根据需求分析函数的实现
- 首先看到 app实际上就是一个对象,里面肯定有两个函数,一个是 use函数,用来注册中间件的,一个是 compose函数,用来启动中间件的,那么注册的中间件会存放在哪里呢,因此肯定要有一个数组来存放已经注册的中间件,满足这些条件的 app有两种设计:
单例模式设计(感觉叫对象设计也可以)
var app = {
middlewares:[],
use:function(){},
compose:function(){}
}
闭包设计
var app = (function(){
var middlewares = []
return {
use:function(){},
compose:function(){}
}
})();
- 这两个设计的区别在于 缓存中间件的数组外界可不可以直接获取,单例模式设计下 通过 app.middlewares 可以直接获取,但是 闭包设计不可以获取,因为不想已经注册的中间件被外界直接修改,所以使用闭包设计会更好
use的实现
use:function(fn){
middlewares.push(fn)
}
compose的实现
- 在实现之前要想明白需求:一旦调用 compose函数,就会调用第一个中间件,并且一旦调用 next,那么就会启动下一个中间件
compose:function(){
var index = 0
var fn = middlewares[index]
function next(){
index++
fn = middlewares[index]
fn && fn(next)
}
fn(next)
}
- 根据需求 compose函数的实现就是根据 思路写出来的,测试一下:
- 跟预想的一样,基本上中间件的核心原理就已经实现了,但是可能会觉得 代码有点丑陋,是因为 有些代码重复了,优化之后:
compose:function(){
function donext(index){
var fn = middlewares[index]
fn && fn(()=>donext(index+1))
}
donext(0)
}
- 很明显,优化之后的代码看起来更简洁,但是思路是一样的,至于怎么想到这么优化的,我只想说就是,靠经验,我觉得优化前的版本才是 正常人写出来的版本
完整代码
var app = (function(){
var middlewares = []
return {
use:function(fn){
middlewares.push(fn)
},
compose:function(){
function donext(index){
var fn = middlewares[index]
fn && fn(()=>donext(index+1))
}
donext(0)
}
}
})();