Description
什么是洋葱模型
洋葱模型就是说,从表皮切入,先从外至内一层层穿过,直到中心层;然后再从内至外,一层层穿出去。进来的时候穿过多少层,出去的时候就相应穿出多少层。这个过程有点类似栈的数据结构,它符合「先进后出」的特点。
中间件
洋葱模型在 Node 的体现就是中间件,其中的关键就是 next 函数,以热门框架 Express 为例:
const express = require('express')
const app = express()
app.use((req, res, next) => {
console.log('enter1')
next()
console.log('exit1')
})
app.use((req, res, next) => {
console.log('enter2')
next()
console.log('exit2')
})
app.use((req, res, next) => {
console.log('enter3')
next()
console.log('exit3')
})
上面这段代码执行,输出结果的顺序如下:
enter1
enter2
enter3
exit3
exit2
exit1
以上就是中间件的大致执行过程。
express 中间件的原理
要理解中间件的原理,就是要知道 app.use
执行后发生了什么。app.use 源码存放于 express/lib/application.js
里,进到这个文件后,全局搜索 app.use
就可以找到函数,这里摘取一部分源码:
当调用 app.use 时,进行一系列处理后,最终调用的是 router.use 。而 router.use 存放于 express/lib/router
目录下,核心代码如下:
这里面做的东西就是,遍历传入的中间件函数 fn,然后创建 layer 对象,将它 push 到栈里面。那下面我们就要去找,这个函数是在哪里,什么时候从栈里面弹出来执行的,这个链路是这样子:
请求进来 --> app.handler --> router.handler --> proto.handle
在 express/lib/router/index.js
里找到 proto.handle 函数,它的核心代码如下:
这里面调用了 layer. handle_request 进行处理,这个函数的源码如下:
可以看到,里面就是 try...catch 执行了 fn 函数,然后把中间件函数 next 作为 fn 的第三个参数传递下去,这就是 express 中间件的执行过程。
koa 中间件的原理
koa 中间件的核心就是 koa-compose 模块中的这段代码:
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
核心就是从第 0 个 middleware 函数开始,然后利用递归的特性,先深入各个中间件函数执行,然后在「归」回来的时候,接着执行剩余代码。