字数
821 字
阅读时间
4 分钟
express 缺点
express 的缺点:
- 重路由的中间件设计。在
express中所有中间件都会通过path-to-regexp去匹配路由正则,造成一定的性能下降 (较为有限)。 querystring默认中间件。在 express 中,每次请求都内置中间件解析 qs,造成一定的性能下降 (在 koa 中为按需解析)。- 无 Context 的设计。express 把数据存储在
req中,当然也可自定义req.context用以存储数据。 res.send直接扔回数据,无ctx.body灵活。- 源码较难理解,且语法过旧,无 koa 代码清晰。
express默认集成了许多中间件,如 static
匹配中间件
js
// 对中间件的一层抽象
class Layer {
//
// 当注册路由 app.use('/users/:id', () => {}) 时,其中以下两个想想为 path 和 handle
// path: /users/:id
// handle: () => {}
constructor (path, handle, options) {
this.path = path
this.handle = handle
this.options = options
this.keys = []
// 根据 path,生政正则表达式
this.re = pathToRegexp(path, this.keys, options)
}
// 查看请求路径是否匹配该中间件,如果匹配,则返回匹配的 parmas
match (url) {
const matchRoute = regexpToFunction(this.re, this.keys, { decode: decodeURIComponent })
return matchRoute(url)
}
}收集中间件
js
class Application {
// 注册应用级中间件,收集所有的应用级中间至 this._router.stack 中,后将实现洋葱模型
use (path, ...fns) {
this._router.use(path, ...fns)
}
// 处理 http 的各种 verb,如 get、post、
// 注册匿名应用级中间件
get (path, ...fns) {
const route = this._router.route(path)
// 对于该应用级中间件所涉及到的所有路由级中间件,在 Route.prototype.get 中进行处理
route.get(...fns)
}
}
class Router {
constructor () {
// 收集所有应用级中间件
this.stack = []
}
dispatch (req, res, done) {
let index = 0
const stack = this.stack
const next = () => {
const layer = stack[index++]
// 如果最后一个
if (!layer) { done() }
layer.handle(req, res, next)
}
next()
}
// 应用级中间件洋葱模型的实现
handle (req, res) {
const stack = this.stack
let index = 0
// 调用下一个应用级中间件
const next = () => {
let layer
let match
while (!match && index < this.stack.length) {
layer = stack[index++]
// 查看请求路径是否匹配该中间件,如果匹配,则返回匹配的 parmas
match = layer.match(req.url)
}
// 遍历中间件,如果无一路径匹配,则状态码为 404
if (!match) {
res.status = 404
res.end('NOT FOUND SHANYUE')
return
}
req.params = match.params
// 处理中间件的函数,如果中间件中调用了 next(),则往下走下一个中间件
layer.handle(req, res, next)
}
next()
}
//
// app.use('/users/', fn1, fn2, fn3)
// 此处路径在 express 中可省略,则默认为所有路径,为了更好地理解源码,此处不作省略
use (path, ...fns) {
for (const fn of fns) {
const layer = new Layer(path, fn)
this.stack.push(layer)
}
}
// 注册应用级路由中间件,是一个匿名中间件,维护一系列关于该路径相关的路由级别中间件,
route (path) {
const route = new Route(path)
// 该匿名中间件的 handleRequest 函数为将应用级中间挂载下的所有路由中间件串联处理
// 对于路由级中间件,完全匹配,即 /api 将仅仅匹配 /api
const layer = new Layer(path, route.dispatch.bind(route), { end: true })
layer.route = route
this.stack.push(layer)
return route
}
}