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

本筆記共四篇
Koa源碼閱讀筆記(1) -- co
Koa源碼閱讀筆記(2) -- compose
Koa源碼閱讀筆記(3) -- 服務器の啓動與請求處理
Koa源碼閱讀筆記(4) -- ctx對象javascript

原由

前兩天終於把本身一直想讀的Koa源代碼讀了一遍。
今天就要來分析Koa的ctx對象,也就是在寫中間件和處理請求和響應時的那個this對象。
而這個this對象,也是和Express的重要區別之一。不用再區分req,res(雖然仍是得知道),一個this對象就能調用全部方法。
在實際開發中,是很是便利的。前端

Koa1和Koa2的區別

在這兒則須要談一談Koa1和Koa2調用this對象的區別。
Koa1在調用時,使用的是this,而Koa2則是ctx。java

// Koa1
app.use(function * (next) {
  this.body = 'hello world'
  yield next
})
// Koa2
app.use(async (ctx, next) => {
  ctx.body = 'hello world'
  await next()
})

使用方式,只是把this換成了ctx。
具體爲何出現ctx和next,以前的文章koa-compose的分析有寫。segmentfault

ctx對象的做用

這兒繼續以Koa1爲例,由於看得懂Koa1源代碼的,看Koa2的源碼天然也不難。
首先放上關鍵的源代碼:api

app.callback = function(){
  var fn = co.wrap(compose(this.middleware));
  var self = this;

  return function(req, res){
    res.statusCode = 404;
    var ctx = self.createContext(req, res);
    onFinished(res, ctx.onerror);
    fn.call(ctx).then(function () {
      respond.call(ctx);
    }).catch(ctx.onerror);
  }
};

在上一篇Koa源碼閱讀筆記(3) -- 服務器の啓動與請求處理中,咱們已經分析了fn的做用。
而onFinished則會在請求完成時調用,剩下的則是調用中間件去處理響應。
同時var ctx = self.createContext(req, res);這一句,不看createContext這個函數,應該也能猜出它的做用。
以後的fn.call(ctx)則說明了中間件中this的來源。
在這兒不得不感嘆一句,JavaScript的this真的是太靈活了,配合閉包,call,apply等,簡直擁有無限魔力。服務器

ctx對象的建立

貼出相關的源代碼:cookie

var response = require('./response');
var context = require('./context');
var request = require('./request');

/**
 * Initialize a new context.
 *
 * @api private
 */

app.createContext = function(req, res){
  var context = Object.create(this.context);
  var request = context.request = Object.create(this.request);
  var 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.onerror = context.onerror.bind(context);
  context.originalUrl = request.originalUrl = req.url;
  context.cookies = new Cookies(req, res, {
    keys: this.keys,
    secure: request.secure
  });
  context.accept = request.accept = accepts(req);
  context.state = {};
  return context;
};

雖然看上去有點繞,可是仔細看看,仍是不難的。
以前說過,Koa的源碼簡潔,一共就4個文件。
除了主要的Application.js, 剩下就都是與請求和響應相關的了。閉包

有趣的地方

這兒,由於每次都要建立並調用ctx對象。爲了不影響原有的context,request,response對象。
這兒採用了Object.create()來克隆對象。app

2016-08-02_14:52:55.jpg

context.js

首先就來分析,最開始的context.js。
context的實現很簡單,但有意思的地方在於delegate這個地方。
就以下圖所示:
2016-08-02_14:45:30.jpgkoa

我看了delegate這個源代碼,功能是把context中相應的方法調用和屬性讀取,委託至某個對象中。
而不用本身一個一個的寫apply,call等。

request, response

關於request和response,我這兒就不詳細寫了。
在這兒放一張圖足以。

2016-08-02_14:56:29.jpg

實際上,request和response是經過getter和setter,來實現存取不一樣屬性的功能。
另外,經過剛纔說的delegate方法,則使用ctx對象時,便能自動經過getter和setter獲取想要的內容。

結語

這一篇很簡單,其實也沒啥能夠說的。
由於Koa除了中間件部分看起來複雜,其它地方仍是很簡潔明瞭的。
學習源代碼的過程當中,也發現了不少優雅的寫法,算是開拓了本身的眼界。
從會寫到寫好,看來還要挺長一段時間的。


前端路漫漫,且行且歌。

最後附上本人博客地址和原文連接,但願能與各位多多交流。

Lxxyx的前端樂園
原文連接:Koa源碼閱讀筆記(4) -- ctx對象

相關文章
相關標籤/搜索