美文网首页
2023.27 koa源码-编写miniKoa

2023.27 koa源码-编写miniKoa

作者: wo不是黄蓉 | 来源:发表于2023-07-25 11:15 被阅读0次

大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。

可以以通过new关键字创建,说明是个构造函数,也可以用es6class来实现,内置uselisten方法,需要构造函数支持这两个方法


//创建koa对象,内置use和listen方法
class Koa {
    constructor() {}
    use() {
        console.log('use')
    }
    listen() {
        console.log('listen')
    }
}
module.exports = Koa

创建好后,执行index.cjs打印


use
use
listen

index.cjs 测试文件内容


// const Koa = require('koa')
const Koa = require('./koa/index.cjs')
const app = new Koa()

//默认中间件函数是个异步函数,如果是同步的会怎么样
app.use((ctx, next) => {
    // app.emit('sayHello', 'hello world')
    console.log('1')
    next()
    console.log('2')
    ctx.body = 'Hello World'
})

app.use((ctx) => {
    console.log('3')
    // ctx.body = 'hello'
})
// app.on('sayHello', (args) => {
//  console.log(111, args)
// })
app.listen(3000)

实现listen方法。创建一个服务并且启动


//创建服务  
listen() {
        const server = http.createServer(this.requestListener)
        server.listen(3001, '0.0.0.0', () => {
            console.log('server is running on :', 'http://localhost:3001')
        })
    }
//请求回调函数,当发起请求时会被调用
    requestListener(req, res) {
        //当请求时返回hello world,请求http://localhost:3001页面会打印出来hello world
        res.end('hello world')
    }
1690289084734.png

处理中间件


    use(middleware) {
        //校验middleware是否是个函数
        if (!Utils.isFunction(middleware)) {
            throw new TypeError('middleware 必须是个函数!')
        }
        this.middleware.push(middleware)
    }

listen完请求时requestListener方法中this丢失问题

1690335642176.png

用箭头函数,使用function会形成一个自己的作用域会造成this丢失,导致调不到this.handleRequest方法

    //请求监听函数
    requestListener() {
        //处理请求监听函数,也就是compose函数
        const fn = compose(this.middleware)

        //没有触发就调用了handleRequest方法,使用箭头函数,将this指向父级作用域
        const handleRequest = (req, res) => {
            const ctx = {
                request: req,
                response: res
            }
            return this.handleRequest(ctx, fn)
        }

        return handleRequest
    }

handleRequest方法,处理请求函数和处理响应,执行fn方法就是触发compose方法实现挨个调用中间件函数

    //请求处理函数
    handleRequest(ctx, fn) {
        //根据上下文执行fn
        //第一次执行第0个监听函数
        //执行compose函数,这边是个函数,不然第一次不会执行,也不会按照顺序执行compose返回结果是个promise
        fn(ctx)
    }

compose方法,我是放在koa对象外面的,也可以当成一个工具类方法

//中间件挨个调用函数
function compose(middlewares) {
    return function (context, next) {
        //第一次执行fn(0)
        return dispatch(0)
        function dispatch(i) {
            //只能执行第一个函数
            let middlewareFn = middlewares[i]
            //当i和中间件长度相等时退出,可执行的函数为空
            if (i === middlewares.length) middlewareFn = next
            //不判断的话会循环执行,i一直在++,i终止的条件就是中间件函数执行完毕了
            if (!middlewareFn) return Promise.resolve()
            //返回一个函数异步函数,dispatch.bind 绑定参数,可以在调用函数时,预先传入一些参数,
            return Promise.resolve(middlewareFn(context, dispatch.bind(null, i + 1)))
        }
    }
}
1690336135213.png

处理响应

koa是通过getter/ setter来实现代理的,只是对原生的request,response做了封装。再将requestresponse对象都封装在context中,通过ctx.body返回响应内容,但是response上本上没有body属性,因此需要通过自定义封装来扩展body属性。

源码中为了方便代理,引入了delegates库,通过delegate实现对responserequest的代理。

response.js 封装res.end用来设置body


module.exports = {
    get body() {
        return this._body
    },
    set body(val) {
        this._body = val
        //原生是通过res.end返回响应的
        this.res.end(val)
    }
}

context.js 通过response来代理context


module.exports = {
    get body() {
        this.response.body
    },
    set body(val) {
        this.response.body = val
    }
}

设置上下文,将requestresponse设置到conetxt


    //创建上下文
    createContext(req, res) {
        //将原有的方法做转换
        const context = Object.create(this.context)
        const request = (context.request = Object.create(this.request))
        const 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.originalUrl = request.originalUrl = req.url
        context.state = {}
        return context
    }

request,response,context关联到koa对象上

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


    constructor() {
        //保存中间件
        this.middleware = []
        this.context = Object.create(context)
        this.request = Object.create(request)
        this.response = Object.create(response)
    }

重启项目

1690340303886.png

这样一个简单的koa实现就好了,源码中还有一些错误处理的地方,属于优化部分,整体流程大概就是这样的。
源码地址

小结

整体代码不是很难,但是可以给我们提供一种代码编程思路,当你想对原有的东西进行扩展api时就可以使用setter/getter代理来实现新属性的代理,这样不仅不会影响原有的东西,而且还能更灵活方便的处理原有的api操作。

相关文章

  • Koa源码阅读

    Koa源码 继着上文的Co源码以及与Koa的深入理解,开始学习下Koa是怎么写的。我看的版本是1.1.2的。 从p...

  • 十分钟带你看完 KOA 源码

    前段时间看了 koa 源码,得益于 koa 良好抽象,不仅提供了简洁的 api ,同时也使得源码相当的简洁和优雅。...

  • 2018-06-15

    Koa Koa2用来编写后台接口文档 puppeteer 爬取数据 mongoDB数据库存储数据

  • koa中间件编写

    如何编写koa框架的中间价? 使用过koa2的朋友们都知道,koa2依赖的中间件不少1、koa-router2、k...

  • TKoa - 使用 TypeScript 重构的 Node.js

    ?Tkoa是使用 typescript 编写的 koa 框架! 尽管它是基于 typescript 编写,但是你依...

  • koa源码

    koa 源码学习记录。因为代码量非常少,所以也很好理解。 http.createServer([requestLi...

  • koa连接MySQL

    这个教程不管node,express,koa都可以用下面方法连接,这里用koa做个参考 源码地址:https://...

  • koa2源码解析

    Koa源码解析 整体架构 核心文件只有4个,在lib文件夹下: application.js koa框架的入口...

  • koa连接mysql

    这个教程不管node,express,koa都可以用下面方法连接,这里用koa做个参考 源码地址:https://...

  • 3.4 分析koa-static的源码

    本节我们将分析 koa-static 的源码。重点不在 熟记源码,而是掌握源码分析的方法。 如何看源码? 一般基于...

网友评论

      本文标题:2023.27 koa源码-编写miniKoa

      本文链接:https://www.haomeiwen.com/subject/gbfhpdtx.html