解析Koa2核心原理(手寫KOA框架并解析)
前言:
相對(duì)于express框架寺庄,koa框架只是提供了async/await 的語法應(yīng)用族沃,大致上邏輯是相同的甚纲!
文章主要分為三個(gè)部分:
- koa2創(chuàng)建服務(wù)
- async/await 語法
- 自己寫個(gè)koa2
一:koa2
使用koa2創(chuàng)建服務(wù)
/**
* koa2 中間件原理
*1. app.use 先將注冊(cè)的中間件收集起來
*2.實(shí)現(xiàn)next。上一個(gè)通過next實(shí)現(xiàn)觸發(fā)執(zhí)行下一個(gè)楞件,
* 等await后面的的函數(shù)執(zhí)行完了在執(zhí)行剩下的語句
*/
const Koa = require('koa');
const app = new Koa();
// 注冊(cè)中間件
// logger
app.use(async (ctx, next) => {
await next();
console.log('第一個(gè)中間件 結(jié)束')
});
app.use(async (ctx, next) => {
await next();
console.log('第二個(gè)中間件 結(jié)束')
});
// response
app.use(async ctx => {
ctx.body = 'Hello World';
console.log('第三個(gè)中間件 結(jié)束')
});
//監(jiān)聽
app.listen(8000);
通過以上代碼可以知道兴蒸,我們先執(zhí)行第一個(gè)中間件视粮,遇到next呢细办,然后去執(zhí)行第二個(gè)中間件橙凳,等后面的中間件都執(zhí)行ok后,await解禁笑撞,執(zhí)行第一個(gè)中間件的剩下的語句
二: async/await 語法
通常在異步的時(shí)候岛啸,我們都會(huì)使用Promise來,但是了多層的嵌套出現(xiàn)問題茴肥,那么如何讓異步“同步化”坚踩?這里是使用 async/await的語法,當(dāng)然瓤狐,返回的是promise對(duì)象
/**
* async timeout(){
* return "hello world"
* throw new Error('rejected');
* }
*
* 其 也是函數(shù)瞬铸,所以,直接調(diào)用础锐,但是嗓节,返回的是promise對(duì)象
*timeout() =====>promise
*
* 如果有正確的返回值,那么就調(diào)用promise.solve(data)
* 如果返回錯(cuò)誤信息皆警, promise.reject(data)
*
* 我們想要在里面執(zhí)行異步函數(shù)讀取數(shù)據(jù)拦宣,
* await promise
* 為了防止錯(cuò)誤,一般我們會(huì)try catch 包裹
*/
三:自己寫KOA2框架
const http = require('http');
class IKoa {
constructor() {
this.middlewareList = [];
}
/**
* 收集 async中間件
*/
use(fn){
this.middlewareList.push(fn)
}
//創(chuàng)建上下文,組合相關(guān)的請(qǐng)求來和去的數(shù)據(jù)
createContext(req,res){
const ctx ={
req,
res
}
ctx.body = (data) => {
let _d = data;
if(typeof _d != 'string'){
_d = JSON.stringify(_d)
}
res.end(_d)
}
return ctx;
}
//組合中間件,定義next方法
compose(middlewareList){
return function(ctx){
// ctx.body({
// name:"a"
// })
function next(i) {
//取出一個(gè)中間件
const fn = middlewareList[i]
if(fn){
//next.bind(null,i+1) = = next() 執(zhí)行下一個(gè)中間件
//這里也就是執(zhí)行中間件函數(shù) dispatch相當(dāng)于next
fn(ctx, next.bind(null, i + 1)) // promise
}
}
//先執(zhí)行第一個(gè)
return next(0)
}
}
/**
* 上下文
* @param {[type]} ctx
* @param {Function} fn 第一個(gè)中間件
* @return fn 這里返回一個(gè)fn
*/
handleRequest(ctx,fn){
return fn(ctx)
}
callback(){
//獲取第一個(gè)中間件
const fn = this.compose(this.middlewareList)
return (req,res) => {
const ctx = this.createContext(req,res);
//這里返回第一個(gè)中間件鸵隧,作為callback的返回
return this.handleRequest(ctx,fn)
}
}
listen(...agrus){
//這里的核心绸罗,callback 返回是執(zhí)行的第一個(gè)中間件,通過第一個(gè)中間件去next下一個(gè)中間件
const server = http.createServer(this.callback());
server.listen(...agrus)
}
}
module.exports = IKoa
通過以上代碼豆瘫,可以看出珊蟀,callback返回的是第一個(gè)中間件的函數(shù)的組裝函數(shù),在這個(gè)組裝函數(shù)里面先執(zhí)行自身外驱,其參數(shù)是ctx上下文系洛,和下一個(gè)中間件函數(shù),其實(shí)next也就是下一個(gè)中間件函數(shù)略步,那么執(zhí)行next就相當(dāng)于執(zhí)行下一個(gè)中間件描扯,當(dāng)然,返回的是Promise趟薄,那么就可以用await接收
測(cè)試使用:
const Koa = require('./i-koa');
const app = new Koa();
app.use( async (ctx, next) => {
// ctx.body("111")
next()
console.log("111");
})
app.use( async (ctx, next) => {
ctx.body("222")
})
app.listen(9000,()=>{
console.log("啟動(dòng)了");
})