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

715 阅读3分钟

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

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

往期更文

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

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

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

前言

  • 回顾昨天的内容,在我们new Application的时候,分离了每个应用实例的上下文对象,使各个实例之间互不影响。
  • 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)
      }
      handleRequest = (req, res) => {
        res.statusCode = 200
        res.setHeader('content-type','text/plain;charset=utf-8')
        res.end('自己实现的Koa')
      }
      listen(){
        const server = http.createServer(this.handleRequest)
        server.listen(...arguments)
      }
    }
    
    module.exports = Application
    

我们在use中使用的ctx上下文对象是何时创建的?

  • 我们先来看一段代码,开启一个3000的端口,当我问访问localhost:3000/get的时候,会返回Hello World get,当我问访问localhost:3000/post的时候,会返回Hello World post,返回结果如下。

    const Koa = require('koa')
    const app = new Koa()
    
    app.use((ctx) => {
      if (ctx.path === '/get') {
        ctx.body = "Hello World get"
      }else if(ctx.path === '/post'){
        ctx.body = "Hello World post"
      }
    })
    
    app.listen(3000, () => {
      console.log('server start 3000')
    })
    

    01-20-2.png 01-20-1.png

  • 从上面的代码,我们可以想象到,肯定是在我们请求的时候,根据每次的请求不同来创建的ctx对象,否则也不能根据每次的请求不同来做不同的响应。事实上koa中也是这么做的,在每次请求来临的时候,在handleRequest中再来创建此次请求的上下文对象,提供给用户使用。

handleRequest请求处理函数之创建每次请求的ctx上下文对象

  • handleRequest,是我们处理请求的函数,第一步我们需要完成的就是要创建每次的请求上下文对象ctx。我们把这个单独的功能抽离出来,在createContext方法中来处理。
  • createContext分别做了以下事情。
      1. 因为每次请求都是不同的,所以每一次请求都需要创建不同的ctx对象,这和每个实例之间分离context是一样的思路。
      1. ctx.request赋值为我们扩展的request请求对象,把原生的请求对象req分别给ctx.request.reqctx.req同步一份。(至于为什么要这么做,我们后面会解释)
      1. ctx.response赋值为我们扩展的response请求对象,把原生的请求对象req分别给ctx.response.resctx.res同步一份。(至于为什么要这么做,我们后面会解释)
    • 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 是我们自己扩展
          //1. 每一次请求都需要创建不同的`ctx`对象
          const ctx = Object.create(this.context)
          const request = Object.create(this.request)
          const response = Object.create(this.response)
          //2. 把`ctx.request`赋值为我们扩展的`request`请求对象,
          //   把原生的请求对象`req`分别给`ctx.request.req`和`ctx.req`同步一份。
          ctx.request = request 
          ctx.request.req = ctx.req = req
      
          //3. 把`ctx.response`赋值为我们扩展的`response`请求对象,
          //   把原生的请求对象`req`分别给`ctx.response.res`和`ctx.res`同步一份。
          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
      

最后

看到这里的兄弟给点个赞吧,万分感谢。

今天是手写koa的第四天,主要还是为了更文的奖品,哈哈,加油,笔者也感觉每天在不停的巩固与学习中,收获很大。加油还有36天。