成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Koa2源碼閱讀筆記

plus2047 / 2503人閱讀

摘要:引言最近空閑時(shí)間讀了一下的源碼在閱讀的源碼的過(guò)程中,我的感受是代碼簡(jiǎn)潔思路清晰不得不佩服大神的水平。調(diào)用的時(shí)候就跟有區(qū)別使用必須使用來(lái)調(diào)用除了上面的的構(gòu)造函數(shù)外,還暴露了一些公用的,比如兩個(gè)常見(jiàn)的,一個(gè)是,一個(gè)是。

引言

最近空閑時(shí)間讀了一下Koa2的源碼;在閱讀Koa2(version 2.2.0)的源碼的過(guò)程中,我的感受是代碼簡(jiǎn)潔、思路清晰(不得不佩服大神的水平)。
下面是我讀完之后的一些感受。

Koa的設(shè)計(jì)理念

Koa 是一個(gè)輕量級(jí)的、極富表現(xiàn)力的 http 框架。
一個(gè)web request會(huì)通過(guò) Koa 的中間件棧,來(lái)動(dòng)態(tài)完成 response 的處理。
Koa2 采用了 async 和 await 的語(yǔ)法來(lái)增強(qiáng)中間件的表現(xiàn)力。
Koa 不在內(nèi)核方法中綁定任何中間件,它僅僅提供了一個(gè)輕量?jī)?yōu)雅的函數(shù)庫(kù)。

Koa基本組成

Koa源碼非常精簡(jiǎn),只有四個(gè)文件:

application.js:框架入口;負(fù)責(zé)管理中間件,以及處理請(qǐng)求

context.js:context對(duì)象的原型,代理request與response對(duì)象上的方法和屬性

request.js:request對(duì)象的原型,提供請(qǐng)求相關(guān)的方法和屬性

response.js:response對(duì)象的原型,提供響應(yīng)相關(guān)的方法和屬性

application.js
// application.js

module.exports = class Application extends Emitter {
  constructor() {
    super();

    this.proxy = false; // 是否信任 proxy header 參數(shù),默認(rèn)為 false
    
    this.middleware = []; //保存通過(guò)app.use(middleware)注冊(cè)的中間件
    
    this.subdomainOffset = 2; // 子域默認(rèn)偏移量,默認(rèn)為 2
    
    this.env = process.env.NODE_ENV || "development"; // 環(huán)境參數(shù),默認(rèn)為 NODE_ENV 或 ‘development’
    
    this.context = Object.create(context); //context模塊,通過(guò)context.js創(chuàng)建
    
    this.request = Object.create(request); //request模塊,通過(guò)request.js創(chuàng)建
    
    this.response = Object.create(response); //response模塊,通過(guò)response.js創(chuàng)建
  }

  // ...
}

application.js 是 koa 的入口主要文件,暴露應(yīng)用的 class, 這個(gè) class 繼承自 EventEmitter ,這里可以看出跟 koa1.x 的不同,koa1.x 是用的是構(gòu)造函數(shù)的方式,koa2 大量使用 es6 的語(yǔ)法。調(diào)用的時(shí)候就跟 koa1.x 有區(qū)別

var koa = require("koa");
// koa 1.x
var app = koa();
// koa 2.x
// 使用class必須使用new來(lái)調(diào)用
var app = new koa();

application.js除了上面的的構(gòu)造函數(shù)外,還暴露了一些公用的api,比如兩個(gè)常見(jiàn)的,一個(gè)是listen,一個(gè)是use。

use函數(shù)
// application.js

use(fn) {
  if (typeof fn !== "function") throw new TypeError("middleware must be a function!");
  if (isGeneratorFunction(fn)) {
    deprecate("Support for generators will be removed in v3. " +
              "See the documentation for examples of how to convert old middleware " +
              "https://github.com/koajs/koa/blob/master/docs/migration.md");
    fn = convert(fn);
  }
  debug("use %s", fn._name || fn.name || "-");
  this.middleware.push(fn);
  return this;
}

use函數(shù)做的事很簡(jiǎn)單:注冊(cè)一個(gè)中間件fn,其實(shí)就是將fn放入middleware數(shù)組。

listen函數(shù)
// application.js

listen(...args) {
  debug("listen");
  const server = http.createServer(this.callback());
  return server.listen(...args);
}

listen方法首先會(huì)通過(guò)this.callback方法來(lái)返回一個(gè)函數(shù)作為http.createServer的回調(diào)函數(shù),然后進(jìn)行監(jiān)聽(tīng)。我們已經(jīng)知道,http.createServer的回調(diào)函數(shù)接收兩個(gè)參數(shù):reqres,下面來(lái)看this.callback的實(shí)現(xiàn):

// application.js

callback() {
  const fn = compose(this.middleware);
    
  if (!this.listeners("error").length) this.on("error", this.onerror);
    
  const handleRequest = (req, res) => {
    res.statusCode = 404;
    const ctx = this.createContext(req, res);
    const onerror = err => ctx.onerror(err);
    const handleResponse = () => respond(ctx);
    onFinished(res, onerror);
    return fn(ctx).then(handleResponse).catch(onerror);
  };
    
  return handleRequest;
}

首先,callback方法把所有middleware進(jìn)行了組合,使用了koa-compose,我們來(lái)看一下koa-compose的代碼:

// koa-compose

function compose (middleware) {
// 傳入的middleware必須是一個(gè)數(shù)組
  if (!Array.isArray(middleware)) throw new TypeError("Middleware stack must be an array!")
// 傳入的middleware的每一個(gè)元素都必須是函數(shù)
  for (const fn of middleware) {
    if (typeof fn !== "function") throw new TypeError("Middleware must be composed of functions!")
  }

  return function (context, next) {
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error("next() called multiple times"))
      index = i
      let fn = middleware[i]
      //下面兩行代碼是處理最后一個(gè)中間件還有next的情況的,其實(shí)就是直接resolve出來(lái)
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve() 
      try {
        // 這里就是傳入next執(zhí)行中間件代碼了
        return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

可以看到koa-compose基本就是個(gè)dispatch函數(shù)的遞歸調(diào)用。其中最重要的就是下面這段代碼:

return Promise.resolve(fn(context, function next () {
  return dispatch(i + 1)
}))

這段代碼等價(jià)于:

fn(context, function next () {
 return dispatch(i + 1)
})
return Promise.resolve()

這里middlewareFunction的第二個(gè)參數(shù)(也就是next)是動(dòng)態(tài)傳遞進(jìn)去的信使,它會(huì)調(diào)取dispatch(index)執(zhí)行下一個(gè)的middleware。最后會(huì)返回一個(gè)Resolved(已完成)狀態(tài)的Promise對(duì)象。這個(gè)對(duì)象的作用我們稍后再說(shuō)。

我們先暫時(shí)回到callback方法里面,前面說(shuō)了它先對(duì)middleware進(jìn)行了組合,生成了一個(gè)函數(shù)fn。
然后,callback方法返回http.createServer所需要的回調(diào)函數(shù)handleRequest

handleRequest函數(shù),先把http code默認(rèn)設(shè)置為404,接著利用createContext函數(shù)把node返回的req和res進(jìn)行了封裝創(chuàng)建出context,
然后通過(guò)onFinished(res, onerror)監(jiān)聽(tīng)http response,當(dāng)請(qǐng)求結(jié)束時(shí)執(zhí)行回調(diào)。這里傳入的回調(diào)是context.onerror(err),即當(dāng)錯(cuò)誤發(fā)生時(shí)才執(zhí)行。
最后返回 fn(ctx).then(handleResponse).catch(onerror)的執(zhí)行結(jié)果,這里的fn函數(shù)就是就是組合所有middleware后生成的函數(shù),調(diào)用它執(zhí)行所有middleware后會(huì)返回前面提到的Resolved(已完成)狀態(tài)的Promise對(duì)象,之后執(zhí)行響應(yīng)處理函數(shù)respond(ctx)respond函數(shù)里面也主要是一些收尾工作,例如判斷http code為空如何輸出啦,http method是head如何輸出啦,body返回是流或json時(shí)如何輸出;代碼就不貼了,感興趣的小伙伴自己可以去看一下),當(dāng)拋出異常時(shí)同樣使用 context.onerror(err)處理。

我們可以看看createContext函數(shù)

// application.js

createContext(req, res) {
  const context = Object.create(this.context);
  const request = context.request = Object.create(this.request);
  const response = context.response = Object.create(this.response);
  context.app = request.app = response.app = this;
  context.req = request.req = response.req = req;
  context.res = request.res = response.res = res;
  request.ctx = response.ctx = context;
  request.response = response;
  response.request = request;
  context.originalUrl = request.originalUrl = req.url;
  context.cookies = new Cookies(req, res, {
    keys: this.keys,
    secure: request.secure
  });
  request.ip = request.ips[0] || req.socket.remoteAddress || "";
  context.accept = request.accept = accepts(req);
  context.state = {};
  return context;
}

createContext創(chuàng)建context的時(shí)候,還會(huì)同時(shí)創(chuàng)建request和response,通過(guò)下圖可以比較直觀地看到所有這些對(duì)象之間的關(guān)系。

圖中:

最左邊一列表示每個(gè)文件的導(dǎo)出對(duì)象

中間一列表示每個(gè)Koa應(yīng)用及其維護(hù)的屬性

右邊兩列表示對(duì)應(yīng)每個(gè)請(qǐng)求所維護(hù)的一些列對(duì)象

黑色的線(xiàn)表示實(shí)例化

紅色的線(xiàn)表示原型鏈

藍(lán)色的線(xiàn)表示屬性

通過(guò)上面的分析,我們已經(jīng)可以大概得知Koa處理請(qǐng)求的過(guò)程:當(dāng)請(qǐng)求到來(lái)的時(shí)候,會(huì)通過(guò) req 和 res 來(lái)創(chuàng)建一個(gè) context (ctx) ,然后執(zhí)行中間件。

content.js

content.js 主要的功能提供了對(duì)requestresponse對(duì)象的方法與屬性便捷訪(fǎng)問(wèn)能力。
其中使用了node-delegates(有興趣的可以看一下源碼),將context.requestcontext.response上的方法與屬性代理到context上。
在源碼中,我們可以看到:

// context.js

delegate(proto, "response")
  .method("attachment")
  // ...
  .access("status")
  // ...
  .getter("writable");

delegate(proto, "request")
  .method("acceptsLanguages")
  // ...
  .access("querystring")
  // ...
  .getter("ip");
request.js

request.js 封裝了請(qǐng)求相關(guān)的屬性以及方法。通過(guò) application.js 中的createContext方法,代理對(duì)應(yīng)的 request 對(duì)象。

const request = context.request = Object.create(this.request);
// ...
context.req = request.req = response.req = req;
// ...
request.response = response;

request.req為原生的請(qǐng)求對(duì)象,在 request.js 中屬性的獲取都是通過(guò) ths.req來(lái)獲取的(即 request.req)。

response.js

response.js 封裝了響應(yīng)相關(guān)的屬性以及方法。與 request 相同,通過(guò)createContext方法代理對(duì)應(yīng)的 response 對(duì)象。

const response = context.response = Object.create(this.response);
// ...
context.res = request.res = response.res = res;
// ...
response.request = request;
結(jié)語(yǔ)

關(guān)于Koa2的源碼就先分析到這,希望對(duì)大家有所幫助。
如有不同的看法,歡迎交流!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/83586.html

相關(guān)文章

  • Koa源碼閱讀筆記(4) -- ctx對(duì)象

    摘要:本筆記共四篇源碼閱讀筆記源碼閱讀筆記源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理源碼閱讀筆記對(duì)象起因前兩天終于把自己一直想讀的源代碼讀了一遍。首先放上關(guān)鍵的源代碼在上一篇源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理中,我們已經(jīng)分析了的作用。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4...

    ityouknow 評(píng)論0 收藏0
  • Koa源碼閱讀筆記(2) -- compose

    摘要:于是抱著知其然也要知其所以然的想法,開(kāi)始閱讀的源代碼。問(wèn)題讀源代碼時(shí),自然是帶著諸多問(wèn)題的。源代碼如下在被處理完后,每當(dāng)有新請(qǐng)求,便會(huì)調(diào)用,去處理請(qǐng)求。接下來(lái)會(huì)繼續(xù)寫(xiě)一些閱讀筆記,因?yàn)榭吹脑创a確實(shí)是獲益匪淺。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4) -...

    roland_reed 評(píng)論0 收藏0
  • Koa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理

    摘要:本筆記共四篇源碼閱讀筆記源碼閱讀筆記源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理源碼閱讀筆記對(duì)象起因前兩天閱讀了的基礎(chǔ),和中間件的基礎(chǔ)。的前端樂(lè)園原文鏈接源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4) -- ctx對(duì)象 起因 前兩天閱讀了K...

    mrcode 評(píng)論0 收藏0
  • 教你從寫(xiě)一個(gè)迷你koa-router到閱讀koa-router源碼

    摘要:本打算教一步步實(shí)現(xiàn),因?yàn)橐忉尩奶嗔耍韵群?jiǎn)化成版本,從實(shí)現(xiàn)部分功能到閱讀源碼,希望能讓你好理解一些。 本打算教一步步實(shí)現(xiàn)koa-router,因?yàn)橐忉尩奶嗔?,所以先?jiǎn)化成mini版本,從實(shí)現(xiàn)部分功能到閱讀源碼,希望能讓你好理解一些。希望你之前有讀過(guò)koa源碼,沒(méi)有的話(huà),給你鏈接 最核心需求-路由匹配 router最重要的就是路由匹配,我們就從最核心的入手 router.get...

    yzzz 評(píng)論0 收藏0
  • koa源碼閱讀[1]-koa與koa-compose

    摘要:接上次挖的坑,對(duì)相關(guān)的源碼進(jìn)行分析第一篇。和同為一批人進(jìn)行開(kāi)發(fā),與相比,顯得非常的迷你。在接收到一個(gè)請(qǐng)求后,會(huì)拿之前提到的與來(lái)創(chuàng)建本次請(qǐng)求所使用的上下文。以及如果沒(méi)有手動(dòng)指定,會(huì)默認(rèn)指定為。 接上次挖的坑,對(duì)koa2.x相關(guān)的源碼進(jìn)行分析 第一篇。 不得不說(shuō),koa是一個(gè)很輕量、很優(yōu)雅的http框架,尤其是在2.x以后移除了co的引入,使其代碼變得更為清晰。 express和ko...

    vibiu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<