# 手撕koa,从零掌握koa的实现原理(5)

198 阅读2分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

手撕koa,从零掌握koa的实现原理(5)

往期更文

手撕koa,从零掌握koa的实现原理(1)

手撕koa,从零掌握koa的实现原理(2)

手撕koa,从零掌握koa的实现原理(3)

手撕koa,从零掌握koa的实现原理(4)

前言

  • 回顾昨天的内容,在每次的请求来临的时候,我们创建了每次请求的上下文对象ctxcreateContext函数中是我们的处理逻辑。
  • koa/koa/lib/application.js
    const http = require('http')
    const EventEmitter = require('events')
    const context = require('./context');
    const request = require('./request');
    const response = require('./response');
    class Application extends EventEmitter {
      constructor(){
        super()
        this.context = Object.create(context)
        this.request = Object.create(request)
        this.response = Object.create(response)
      }
      createContext(req, res){
        // req, res 是原生的  request 和 response 是我们自己扩展
        const ctx = Object.create(this.context)
        const request = Object.create(this.request)
        const response = Object.create(this.response)
        ctx.request = request 
        ctx.request.req = ctx.req = req
    
        ctx.response = response; 
        ctx.response.res = ctx.res = res;
    
        return ctx
      }
      handleRequest = (req, res) => {
    
        const ctx = this.createContext(req, res)
    
      }
      listen(){
        const server = http.createServer(this.handleRequest)
        server.listen(...arguments)
      }
    }
    
    module.exports = Application
    

扩展request对象

  • 在每次请的时候,我们虽然创建了上下文对象,但是ctx上还没有任何的属性,下面我们来扩展一下contextrequestresponse,这三个对象。
  • 我们在createContext时候,给request身上添加了req属性,目的就是为了在request对象扩展的时候可以快速地拿到原生的req。我们下面只扩展了三个属性作为示范,在官方的koarequest对象也是这样扩展的。
    • koa/koa/lib/request.js
      const url  = require('url')
      const request = {
        get url(){
          return this.req.url
        },
      
        get path(){
          let {pathname} = url.parse(this.req.url)
      
          return pathname
        },
      
        get query(){
          let {pathname, query} = url.parse(this.req.url, true)
          return query
        }
      }
      module.exports = request
      

扩展response对象

  • response响应对象中我们扩展一下,我们使用的ctx.body,为什么我们给ctx.body赋值,浏览器就会拿到我们的返回结果呢,答案就在这里。
    • koa/koa/lib/response.js
        const response = {
          _body:undefined,
          get body(){
            return this._body
          },
          set body(content){
            this.res.statusCode = 200
            this._body = content
          }
        }  
      

扩展context对象

  • context上下文对象的扩展,koa中使用的是__defineGetter____defineSetter__方法来代理属性的,就是相当于Object.defineProperty方法的getset,从下面的代码我们可以看出来,当我们访问或设置ctx对象上的属性时,会被代理到requestresponse对象上,这样做的作用就是为了,我们在使用的时候能够更简便。

    例如,我要访问ctx.request.path时,可以直接访问ctx.path

    • koa/koa/lib/context.js
      const context = {};
      function defineGetter(target, key) {
        context.__defineGetter__(key,function () {
          return this[target][key]
        })
      }
      
      function defineSetter(target, key) {
        context.__defineSetter__(key,function (value) {
          this[target][key] = value
        })
      }
      defineGetter('request', 'path')
      defineGetter('request', 'url')
      defineGetter('request', 'query')
      
      defineGetter('response', 'body')
      defineSetter('response', 'body')
      module.exports = context;
      

下期预告

koa的中间件实现原理

更文第五天,加油,还剩35天。