Koa源码解析,带你实现一个迷你版的Koa (2)

栈空,全部执行完毕

相信通过这个简单的例子,都大概明白中间件的执行过程了吧。

原理实现

中间件原理实现的关键点主要就是 ctx 和 next 的传递。

因为中间件是可以异步执行的,最后需要返回 Promise。

function compose(middleware{
  return function(ctx{
    return dispatch(0)
    function dispatch(i){
      // 取出中间件
      let fn = middleware[i]
      if (!fn) {
        return Promise.resolve()
      }
      // dispatch.bind(null, i + 1) 为应用中间件接受到的 next
      // next 即下一个应用中间件的函数引用
      try {
        return Promise.resolve( fn(ctx, dispatch.bind(null, i + 1)) )
      } catch (error) {
        return Promise.reject(error)
      }
    }
  }
}

可以看到,实现过程本质是函数的递归调用。在内部实现时,其实 next 没有做什么神奇的操作,它就是下一个中间件调用的函数,作为参数传入供使用者调用。

下面我们来使用一下 compose,你可以将它粘贴到控制台上运行:

function next1(ctx, next{
  console.log('1 start')
  next()
  console.log('1 end')
}
function next2(ctx, next{
  console.log('2 start')
  next()
  console.log('2 end')
}
function next3(ctx, next{
  console.log('3 start')
  next()
  console.log('3 end')
}

let ctx = {}
let fn = compose([next1, next2, next3])
fn(ctx)
完整实现

application.js:

const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')

module.exports = class Coa {
  constructor() {
    this.middleware = []
    this.context = context
    this.request = request
    this.response = response
  }

  use(fn) {
    if (typeof fn !== 'function'throw new TypeError('middleware must be a function!');
    this.middleware.push(fn)
    return this
  }

  listen(...args) {
    const server = http.createServer(this.callback())
    server.listen(...args)
  }

  callback() {
    const handleRequest = (req, res) => {
      // 创建上下文
      const ctx = this.createContext(req, res)
      // fn 为第一个应用中间件的引用
      const fn = this.compose(this.middleware)
      return fn(ctx).then(() => respond(ctx)).catch(console.error)
    }
    return handleRequest
  }

  // 创建上下文
  createContext(req, res) {
    const ctx = Object.create(this.context)
    // 处理过的属性
    const request = ctx.request = Object.create(this.request)
    const response = ctx.response = Object.create(this.response)
    // 原生属性
    ctx.app = request.app = response.app = this;
    ctx.req = request.req = response.req = req
    ctx.res = request.res = response.res = res

    request.ctx = response.ctx = ctx;
    request.response = response;
    response.request = request;

    return ctx
  }

  // 中间件处理逻辑实现
  compose(middleware) {
    return function(ctx{
      return dispatch(0)
      function dispatch(i){
        let fn = middleware[i]
        if (!fn) {
          return Promise.resolve()
        }
        // dispatch.bind(null, i + 1) 为应用中间件接受到的 next
        // next 即下一个应用中间件的函数引用
        try {
          return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
        } catch (error) {
          return Promise.reject(error)
        }
      }
    }
  }
}

// 处理 body 不同类型输出
function respond(ctx{
  let res = ctx.res
  let body = ctx.body
  if (typeof body === 'string') {
    return res.end(body)
  }
  if (typeof body === 'object') {
    return res.end(JSON.stringify(body))
  }
}
写在最后

本文的简单实现了 Koa 主要的功能。有兴趣最好还是自己去看源码,实现自己的迷你版 Koa。其实 Koa 的源码不算多,总共4个文件,全部代码包括注释也就 1800 行左右。而且逻辑不会很难,很推荐阅读,尤其适合源码入门级别的同学观看。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyjxg.html