以上5個概念應該仍是很好理解的vue
首先把koa給下載下來了,能夠看到這裏有四個文件,這裏我主要講下application,也就是最核心的一個模塊 node
這裏核心模塊是application文件,從開始提及吧app
const Emitter = require("events");
class Koa2 extends Emitter {
constructor() {
super();
this.middleware = [];
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
/** * 監聽listen */
listen(...args) {
}
/** * 回調函數 */
callback() {
}
/** * 使用插件 * @param {} fn */
use(fn) {
}
}
複製代碼
以上咱們經常使用的是一些接口,從這些東西入手吧,這裏繼承了emitter這個構造函數,也就是node裏面的事件監聽,用過的人應該使用過下面這種koa
const app = new Koa2
app.on("error",()=>{
})
複製代碼
看到這裏大概就明白了爲何要繼承emitter,主要是爲了實現事件分發async
這裏很簡單,其實就是把use中的函數加到中間件隊列中,都應該能理解了函數
/** * 使用插件 * @param {} fn */
use(fn) {
this.middleware.push(fn);
return this;
}
複製代碼
這裏至關因而一個語法糖ui
/** * 監聽listen */
listen(...args) {
let server = http.createServer(this.callback());
server.listen(...args);
}
/** * 回調函數 */
callback() {
const fn = conpose(this.middleware); //中間件的實現方式
// 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;
}
複製代碼
這裏將全部的中間件聚合成一個函數,這裏聚合使用的是conpose,也就是koa的中間件洋蔥模型this
衆所周知,koa是使用async的, 這裏說的,這裏的dispatch至關於next的意思吧!進入spa
module.exports = function(middleware) {
return function(ctx, next) {
// let index = -1;
return dispatch(0);
function dispatch(i) {
let fn = middleware[i];
if (i === middleware.length) {
fn = next
}
if(!fn) {
return Promise.resolve()
}
try {
return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)));
} catch (error) {
return Promise.reject(error)
}
}
};
};
複製代碼
再回來看callback
這個函數插件
callback() {
const fn = conpose(this.middleware); //中間件的實現方式
// 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, middleware) {
return middleware(ctx).then(() => {
console.log("中間件執行結束");
// 這裏能夠拿到中間件裏面修改的數據等
/* 如ctx.response.body({ }) */
return ctx.response.res.end("hello");
});
}
/** * 建立上下文 */
createContext(req, res) {
// let ctx = {}
const context = Object.create(this.context);
context.request = Object.create(this.request);
context.response = Object.create(this.response);
context.app = this;
context.response.res = res;
context.request.req = req;
return context;
}
複製代碼
req
和res
是http.createServer()的回調函數,這裏就很少說了 下面的重點是建立一個上下文createContext
這裏就連接到Object.create(this.context)
等, 其實也就是將context,request,response實例化,將callback裏面回傳的request,response重寫了一下,而且提供了一些方法 Object.create(this.request)
和Object.create(this.response)
就不細說了,都是封裝一些方法,封裝一些返回的值
進入到context文件,咱們能夠看到下面的這樣一個函數調用,其實就是一個數據劫持
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.method('remove')
.method('vary')
.method('set')
.method('append')
.method('flushHeaders')
.access('status')
.access('message')
.access('body')
.access('length')
.access('type')
.access('lastModified')
.access('etag')
.getter('headerSent')
.getter('writable');
複製代碼
打個比方說ctx.body
,其實就是ctx.response.body
這裏作了一層代理,好處的話,能夠不用污染ctx,而且不須要建立兩份變量,這裏能夠去看看vue的data是怎麼作的,爲何在this
中能訪問到data中的變量,這裏是同樣的
以上就是主要的koa內容了,至於其餘都是開發中間件了 最後還有一個點 就是inspect
,你看代碼的時候會看到在生命對象和構造函數的時候都用到了這個inspect
舉個例子哈!
const proto = {
inspect(){
return "hello inspect";
}
}
if (util.inspect.custom) {
proto[util.inspect.custom] = proto.inspect;
}
let a = Object.create(proto)
console.log(a)
// 輸出 "hello inspect"
複製代碼
koa裏面用到,我理解的意思是,在打印的時候只打印出本身想打印出來的 ,別的不打印,其餘就本身體會了。