基於Nodejs平臺web框架-Express VS Koa

Express和Koa都是基於Nodejs平臺的web框架,也是目前比較常見的用於快速開發web服務的框架,且二者都是基於middleware的方式去處理客戶端請求,那麼二者有何區別呢?
簡單點說就是,「Express是直線型,Koa是洋蔥模型」。(喊口號!!!)
咱們先來看看下面的示例代碼:web

// for express example
const express = require('express');

const app = express();

function cb1(req, res, next) {
    console.log('>>>>>>cb1');
    next();
    console.log('<<<<<<cb1');
}

function cb2(req, res, next) {
    console.log('>>>cb2<<<');
    res.send('hello world');
}

app.use('/', [cb1, cb2]);
app.listen(3000);
// for koa2 example
const koa = require('koa2');

const app = koa();

function cb1(ctx, next) {
    console.log('>>>>>>cb1');
    next();
    console.log('<<<<<<cb1');
}

function cb2(ctx, next) {
    console.log('>>>cb2<<<');
    ctx.body = 'hello world';
}

app.use(cb1);
app.use(cb2);
app.listen(3000);

以上兩段代碼的輸出皆爲:express

>>>>>>cb1
>>>cb2<<<
<<<<<<cb1

因此,當middleware爲同步函數時,二者從執行結果上來看並沒有區別。
咱們再來看看下面的示例代碼:api

// for express example
const express = require('express');

const app = express();

async function cb1(req, res, next) {
    console.log('>>>>>>cb1');
    await next();
    console.log('<<<<<<cb1');
}

async function cb2(req, res, next) {
    return new Promise((resolve) => {
        setTimeout(resolve, 500);
    }).then(() => {
        console.log('>>>cb2<<<');
        res.send('hello world');
    });
}

app.use('/', [cb1, cb2]);
app.listen(3000);
// for koa2 example
const koa = require('koa2');

const app = new koa();

async function cb1(ctx, next) {
    console.log('>>>>>>cb1');
    await next();
    console.log('<<<<<<cb1');
}

async function cb2(ctx, next) {
    return new Promise((resolve) => {
        setTimeout(resolve, 500);
    }).then(() => {
        console.log('>>>cb2<<<');
        ctx.body = 'hello world';
    });
}

app.use(cb1);
app.use(cb2);
app.listen(3000);

express-example的輸出爲:promise

>>>>>>cb1
>>>>>>cb1
>>>cb2>>>

而koa2-example的輸出爲:app

>>>>>>cb1
>>>cb2<<<
<<<<<<cb1

從上面的例子能夠看出,當middleware爲異步函數時,Express和Koa的執行流程是不一樣的。Express的返回結果並非咱們設想中的結果,是什麼緣由致使的行爲差別呢?下面,讓咱們一塊兒來簡單的分析下Express和Koa中執行middleware部分的源碼片斷。
在Express中,執行middleware的邏輯代碼主要位於lib/router/route.jslib/router.layer.js文件:框架

// route.js
Route.prototype.dispatch = function dispatch(req, res, done) {
  var idx = 0;
  var stack = this.stack;
  if (stack.length === 0) {
    return done();
  }

  var method = req.method.toLowerCase();
  if (method === 'head' && !this.methods['head']) {
    method = 'get';
  }

  req.route = this;

  next();

  function next(err) {
    // signal to exit route
    if (err && err === 'route') {
      return done();
    }

    // signal to exit router
    if (err && err === 'router') {
      return done(err)
    }

    var layer = stack[idx++];
    if (!layer) {
      return done(err);
    }

    if (layer.method && layer.method !== method) {
      return next(err);
    }

    if (err) {
      layer.handle_error(err, req, res, next);
    } else {
      layer.handle_request(req, res, next);
    }
  }
};


//layer.js
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
  var fn = this.handle;

  if (fn.length !== 4) {
    // not a standard error handler
    return next(error);
  }

  try {
    fn(error, req, res, next);
  } catch (err) {
    next(err);
  }
};

Layer.prototype.handle_request = function handle(req, res, next) {
  var fn = this.handle;

  if (fn.length > 3) {
    // not a standard request handler
    return next();
  }

  try {
    fn(req, res, next);
  } catch (err) {
    next(err);
  }
};

在Koa2中,執行middleware的邏輯代碼主要位於koa-compose/index.js文件:koa

function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    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]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

由上可知,Express中middleware的next參數是一個普通的函數對象,而Koa中middleware的next參數是一個promise對象。因此當咱們掛載異步的middleware時,Express並不能像Koa同樣,在middleware中使用await去等待下一個middleware執行完成以後,再執行當前middleware的後續邏輯。這就是爲何「Express是直線型,Koa是洋蔥模型」的根本緣由。
以上就是我對於Express和Koa框架的理解,但願對你有幫助。若是上述內容有錯誤的地方,歡迎你們指正,謝謝~異步

相關文章
相關標籤/搜索