簡介
Koa由 Express 幕后的原班人馬打造, 致力于成為 web 應用和 API 開發領域中的一個更小、更富有表現力、更健壯的基石, 通過利用 async 函式,Koa 幫你丟棄回呼函式,同時koa的中間件機制和洋蔥模型是它獨有的特點,并且它沒有捆綁任何中間件,而是可以讓用戶以更優雅的方式去擴展,
Koa是一個精簡的web框架,它主要做了以下幾件事:
- 為request和response物件賦能,并基于它們封裝成一個context物件,可以通過get,set呼叫引數
- 基于async/await的中間件容器機制
原始碼解讀
打開koa的原始碼,核心檔案共四個在lib目錄下:
- application.js
- context.js
- request.js
- response.js
application(初始化)
application是Koa2的入口,里面引入了request.js,response.js,context.js,然后用建構式的形式,暴露出來,
這里列出主要結構代碼:
module.exports = class Application extends Emitter {
constructor(options) {
// xxx
this.middleware = []; // 注:用來存放通過use函式引入的中間件的陣列,
this.context = Object.create(context); // 注:創建content物件
this.request = Object.create(request); // 注:同上
this.response = Object.create(response); // 注:同上
}
listen(...args) { // 注:創建node服務實體
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
use(fn) { // 注: 用來把中間件加入到middleware陣列里面
// xxx
this.middleware.push(fn);
return this;
}
callback() { // 注: 回傳一個函式給node服務,作為處理請求的回呼函式
const fn = compose(this.middleware); // 注: 很重要,將所有的中間件通過compose組合一下,弄成洋蔥模型,這里是Koa的精髓部分
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
handleRequest(ctx, fnMiddleware) { // 注:處理錯誤
// xxx
}
createContext(req, res) { // 注:創建ctx物件,并且把req,res寫入ctx
// xxx
}
content
里面都是一堆對引數的set get設定,方便一些引數的獲取,
const util = require('util');
const createError = require('http-errors');
const httpAssert = require('http-assert');
const delegate = require('delegates');
const statuses = require('statuses');
const Cookies = require('cookies');
const COOKIES = Symbol('context#cookies');
const proto = module.exports = {
// xxx 注:一些方法函式,toJosn之類的,
}
// 注:這里就是為了在application.js 里面createContent 的時候,把一些response,request的物件屬性代理給content,
delegate(proto, 'response')
.method('attachment')
.xxx
delegate(proto, 'request')
.method('acceptsLanguages')
.xxx
request
module.exports = {
// 注:基于req封裝很多便利的函式和屬性,可以讓application.js 里面request物件直接呼叫,
get header() {
return this.req.headers;
},
set header(val) {
this.req.headers = val;
},
response
module.exports = {
// 注:基于res封裝很多便利的函式和屬性,可以讓application.js 里面responset物件直接呼叫,
get headers() {
return this.header;
},
get status() {
return this.res.statusCode;
},
中間件機制koa-compose
介紹application.js的時候,提到里面有個koa-compose,這里是整個koa框架的精髓,
我們所知的koa中間件是一個洋蔥模型:

現在我們來學習compose是怎么實作洋蔥模型的,
要學會洋蔥模型,我們必須先要懂一個函式式編程的東西,就是函式合成和函式柯里化,compose就是運用了函式式編程的方式,來實作洋蔥模型的,
函式的合成與柯里化
- 函式合成:
如果一個值要經過多個函式,才能變成另外一個值,就可以把所有中間步驟合并成一個函式,這叫做"函式的合成"(compose),
const compose = function (f, g) {
return function (x) {
return f(g(x));
};
}
- 柯理化:
f(x)和g(x)合成為f(g(x)),有一個隱藏的前提,就是f和g都只能接受一個引數,如果可以接受多個引數,比如f(x, y)和g(a, b, c),函式合成就非常麻煩,
這時就需要函式柯里化了,所謂"柯里化",就是把一個多引數的函式,轉化為單引數函式,
// 柯里化之前
function add(x, y) {
return x + y;
}
add(1, 2) // 3
// 柯里化之后
function addX(y) {
return function (x) {
return x + y;
};
}
addX(2)(1) // 3
compose實作
學習完函式式編程之后,我們來實作compose函式,
// compose.js
function compose(middlewares) {
return function (ctx) {
return dispatch(0)
function dispatch(i) {
let fn = middlewares[i] // 注:application.js 里面用來存放中間件的陣列
if (!fn) {
return Promise.resolve()
}
return Promise.resolve(fn(ctx,function next() {
return dispatch(i + 1)
}))
}x
}
}
// 寫幾個中間件案例:
async function fn1(ctx,next) {
console.log("fn1");
await next();
console.log("end fn1");
}
async function fn2(ctx,next) {
console.log("fn2");
await next();
console.log("end fn2");
}
function fn3(ctx,next) {
console.log("fn3");
}
const middlewares = [fn1, fn2, fn3];
const finalFn = compose(middlewares);
finalFn('ctx'); // 注:這里ctx隨便用個字串代替,正式呼叫的話,ctx是createContent創建的背景關系物件,里面有response,request等等,

洋蔥模型,先從外往里面執行,然后再從里往外執行,
- ① 先執行fn1,列印fn1
- ② 遇到next函式,fn1后面代碼掛起,等待next1執行完畢(這里命名next1,方便理解)
- ③ next1通過dispatch函式執行fn2
- ④ 列印fn2
- ⑤ 遇到next函式,fn2后面代碼掛起,等待next2執行完畢
- ⑥ next2通過dispatch函式執行fn3
- ⑦ 列印fn3
- ⑧ 此時fn3函式全部走完,通知fn2,next2已經執行完畢,運行fn2后面代碼,列印end fn2
- ⑨ 此時fn2函式也已經全部走完,再通知fn1,next1已經執行完,運行fn1后面的代碼,列印end fn1
到這里一個全部compose就完成了,簡單來說這里就是一個Promise嵌套,加上函式式編程,
想看實作的koa2專案代碼(application,response,request,compose)可以去我的githubk-koa 看完整專案,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/205475.html
標籤:其他
上一篇:css簡單練習5
