JS实现Koa
中间件原理
koa最核心的原理就是中间件原理:过程中通过next对下一个中间件函数的调用,利用这个特性在 next 之前对 request 进行处理,在 next 函数之后对 response 处理。
Koa 的中间件模型可以非常方便的实现后置处理逻辑。
使用示例
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (ctx, next) => {
console.log(3);
await next();
console.log(4);
});
app.use(async (ctx, next) => {
ctx.body = 'Hello, Koa';
console.log('body')
});
app.listen(3001, () => {
console.log('启动成功')
});
输出:1 3 body 4 2
代码执行顺序可以这么理解:
next()前面的代码放到一个队列:[1, 3, body],先进先出
next()后面的代码放到一个栈:[2, 4],后进先出
所以执行顺序就是 1 3 body 4 2
JS实现中间件原理
本质上是递归调用
class Koa {
constructor() {
// 中间件队列
this.middlewareQueue = []
}
// 注册中间件,格式(ctx, next) => {}
use(middileware) {
this.middlewareQueue.push(middileware)
}
// 包装每一个中间件,实现洋葱模型
compose(ctx) {
// next()其实就是dispatch函数
const dispatch = (index) => {
// 递归结束条件
if (index > this.middlewareQueue.length - 1) {
return Promise.resolve()
}
// 第i个中间件函数
const middlewareFn = this.middlewareQueue[index]
// 包装下一个中间件
const next = () => dispatch(index + 1)
// 执行当前中间件
const result = middlewareFn(ctx, next)
// 返回结果给上一个中间件,这里使用Promise返回,中间件就可以定义成异步函数了
return Promise.resolve(result)
}
// 从第一个中间件开始执行
return dispatch(0)
}
// 测试洋葱模型
test() {
console.log(this.middlewareQueue)
this.compose({})
}
}
const app = new Koa()
app.use((ctx, next) => {
console.log(1)
const nextResult = next()
console.log(2, nextResult)
return 'middle 1'
})
app.use((ctx, next) => {
console.log(3)
const nextResult = next()
console.log(4, nextResult)
return 'middle 2'
})
app.test()
Koa原理
koa的组成部分
- 中间件:app.use进行注册,compose实现洋葱模型
- 启动http服务:app.listen,内部调用http.createServer,同时创建context
- context对象:挂载ctx.req、ctx.res
- request对象:通过ctx.req访问,原理是http.createServer((req, res) => {})的req
- response对象:通过ctx.res访问,原理是http.createServer((req, res) => {})的res
koa执行流程:创建服务http.CreateServer -> 创建上下文createContext -> 执行中间件compose() -> 监听端口server.listen(3000)
JS实现Koa
const http = require('http')
class Koa {
constructor() {
// 中间件队列
this.middlewareQueue = []
// 上下文
this.context = null
}
// 创建上下文
createContext(req, res) {
this.context = {
req, // request对象
res // response对象
}
}
// 注册中间件,格式(ctx, next) => {}
use(middileware) {
this.middlewareQueue.push(middileware)
}
// 执行中间件
compose(ctx) {
// next()其实就是dispatch函数,实现洋葱模型
const dispatch = (index) => {
// 递归结束条件
if (index > this.middlewareQueue.length - 1) {
return Promise.resolve()
}
// 第i个中间件函数
const middlewareFn = this.middlewareQueue[index]
// 包装下一个中间件
const next = () => dispatch(index + 1)
// 执行当前中间件
const result = middlewareFn(ctx, next)
// 返回结果给上一个中间件,这里使用Promise返回,中间件就可以定义成异步函数了
return Promise.resolve(result)
}
// 从第一个中间件开始执行
return dispatch(0)
}
// 启动http服务
listen(...args) {
const requestHandler = (req, res) => {
console.log('request coming')
// 创建上下文
this.createContext(req, res)
// 执行中间件
this.compose(this.context)
}
const server = http.createServer(requestHandler)
return server.listen(...args)
}
}
const app = new Koa()
app.use(async (ctx, next) => {
console.log(1)
const nextResult = await next()
console.log(2, nextResult)
return 'middle 1'
})
app.use(async (ctx, next) => {
console.log(3)
const nextResult = await next()
console.log(4, nextResult)
return 'middle 2'
})
app.use(async (ctx, next) => {
ctx.res.end('okk')
await next()
})
app.use(async (ctx, next) => {
console.log('request end')
})
app.listen(3002, () => {
console.log('start server')
})