如何从零开始手写Koa2框架

Koa-- 基于 Node.js 平台的下一代 web 开发框架

Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。

与其对应的 Express 来比,Koa 更加小巧、精壮,本文将带大家从零开始实现 Koa 的源码,从根源上解决大家对 Koa 的困惑

本文 Koa 版本为 2.7.0, 版本不一样源码可能会有变动

02、源码目录介绍

Koa 源码目录截图

如何从零开始手写Koa2框架

通过源码目录可以知道,Koa主要分为4个部分,分别是:

application: Koa 最主要的模块, 对应 app 应用对象

context: 对应 ctx 对象

request: 对应 Koa 中请求对象

response: 对应 Koa 中响应对象

这4个文件就是 Koa 的全部内容了,其中 application 又是其中最核心的文件。我们将会从此文件入手,一步步实现 Koa 框架

03、实现一个基本服务器代码目录

my-application

const {createServer} = require('http'); module.exports = class Application { constructor() { // 初始化中间件数组, 所有中间件函数都会添加到当前数组中 this.middleware = []; } // 使用中间件方法 use(fn) { // 将所有中间件函数添加到中间件数组中 this.middleware.push(fn); } // 监听端口号方法 listen(...args) { // 使用nodejs的http模块监听端口号 const server = createServer((req, res) => { /* 处理请求的回调函数,在这里执行了所有中间件函数 req 是 node 原生的 request 对象 res 是 node 原生的 response 对象 */ this.middleware.forEach((fn) => fn(req, res)); }) server.listen(...args); } }

index.js

// 引入自定义模块 const MyKoa = require('./js/my-application'); // 创建实例对象 const app = new MyKoa(); // 使用中间件 app.use((req, res) => { console.log('中间件函数执行了~~~111'); }) app.use((req, res) => { console.log('中间件函数执行了~~~222'); res.end('hello myKoa'); }) // 监听端口号 app.listen(3000, err => { if (!err) console.log('服务器启动成功了'); else console.log(err); })

运行入口文件 index.js 后,通过浏览器输入网址访问 :3000/ , 就可以看到结果了~~

神奇吧!一个最简单的服务器模型就搭建完了。当然我们这个极简服务器还存在很多问题,接下来让我们一一解决

04、实现中间件函数的 next 方法

提取createServer的回调函数,封装成一个callback方法(可复用)

// 监听端口号方法 listen(...args) { // 使用nodejs的http模块监听端口号 const server = createServer(this.callback()); server.listen(...args); } callback() { const handleRequest = (req, res) => { this.middleware.forEach((fn) => fn(req, res)); } return handleRequest; }

封装compose函数实现next方法

// 负责执行中间件函数的函数 function compose(middleware) { // compose方法返回值是一个函数,这个函数返回值是一个promise对象 // 当前函数就是调度 return (req, res) => { // 默认调用一次,为了执行第一个中间件函数 return dispatch(0); function dispatch(i) { // 提取中间件数组的函数fn let fn = middleware[i]; // 如果最后一个中间件也调用了next方法,直接返回一个成功状态的promise对象 if (!fn) return Promise.resolve(); /* dispatch.bind(null, i + 1)) 作为中间件函数调用的第三个参数,其实就是对应的next 举个栗子:如果 i = 0 那么 dispatch.bind(null, 1)) --> 也就是如果调用了next方法 实际上就是执行 dispatch(1) --> 它利用递归重新进来取出下一个中间件函数接着执行 fn(req, res, dispatch.bind(null, i + 1)) --> 这也是为什么中间件函数能有三个参数,在调用时我们传进来了 */ return Promise.resolve(fn(req, res, dispatch.bind(null, i + 1))); } } }

使用compose函数

callback () { // 执行compose方法返回一个函数 const fn = compose(this.middleware); const handleRequest = (req, res) => { // 调用该函数,返回值为promise对象 // then方法触发了, 说明所有中间件函数都被调用完成 fn(req, res).then(() => { // 在这里就是所有处理的函数的最后阶段,可以允许返回响应了~ }); } return handleRequest; }

修改入口文件 index.js 代码

// 引入自定义模块 const MyKoa = require('./js/my-application'); // 创建实例对象 const app = new MyKoa(); // 使用中间件 app.use((req, res, next) => { console.log('中间件函数执行了~~~111'); // 调用next方法,就是调用堆栈中下一个中间件函数 next(); }) app.use((req, res, next) => { console.log('中间件函数执行了~~~222'); res.end('hello myKoa'); // 最后的next方法没发调用下一个中间件函数,直接返回Promise.resolve() next(); }) // 监听端口号 app.listen(3000, err => { if (!err) console.log('服务器启动成功了'); else console.log(err); })

此时我们实现了next方法,最核心的就是compose函数,极简的代码实现了功能,不可思议!

05、处理返回响应

定义返回响应函数respond

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

转载注明出处:http://www.heiqu.com/6bc4f95d9e7437596509e787aa168e7d.html