资讯动态

Egg 与 Koa:从异步编程模型到企业级框架的演进

发布时间:2026/9/21 16:15:47 来源:尧图企业网站定制
后端Web框架【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址https://gitcode.com/gh_mirrors/eg/egg点击查看免费下载本文以 Egg 官方文档《Egg and Koa》为核心脉络系统梳理 Node.js 异步编程模型Callback → Promise → Generator/co → async/await的演进深入剖析 Koa 的洋葱模型中间件、Context 与异常处理机制并结合本仓库源码说明 Egg 如何在 Koa 之上通过扩展Extend与插件Plugin机制补齐企业级能力同时回顾 Egg 1.x 与 2.x 两个版本在底层异步方案上的路线选择。读完本文你将理解 Egg 与 Koa 的继承关系掌握洋葱模型中间件的执行原理、Context 委托机制并能在自己的 Egg 应用中熟练使用app/extend/*扩展与插件机制。异步编程模型Node.js 世界的起点与演进Node.js 是一个异步世界其官方 API 提供的异步编程模型全部基于回调callback形式这带来了很多问题例如回调地狱callback hell层层嵌套的回调让代码难以阅读、维护与调试是 Node.js 开发者最熟悉的痛点释放 Zalgorelease zalgo异步函数可能同步地调用回调返回数据这种时而同步、时而异步的不一致行为会破坏调用方的状态假设导致难以排查的隐性 bug。社区为解决这些问题提供了大量方案最终胜出的是Promise——它已被内置于 ECMAScript 2015。在 Promise 的基础之上借助Generator 的上下文切换能力配合 co 等第三方库开发者可以用同步书写的方式编写异步代码。与此同时官方方案async function于 ECMAScript 2017 发布并落地到 Node.js 8异步编程进入了新阶段。Async Function语言层面的语法糖Async function 是语言层面的语法糖。在 async function 中可以使用await等待一个 Promise 被 resolve或 reject此时会抛出异常Node.js LTS8.x已原生支持该特性const fn async function () { const user await getUser(); const posts await fetchPosts(user.id); return { user, posts }; }; fn() .then((res) console.log(res)) .catch((err) console.error(err.stack));这段代码以近乎同步的线性书写方式完成两次依赖性的异步请求getUser()的结果通过await直接解包异常则统一由.catch捕获——这正是同步写法、异步执行、统一异常模型的直观体现。Koa更小、更富表现力的 Web 框架Koa 是由 Express 团队设计的新一代 Web 框架目标是作为 Web 应用与 API 的更小、更富表现力、更健壮的基石。Koa 与 Express 的设计风格非常相似底层基础库同源都依赖 HTTP library 生态但两者存在若干显著差异。除了上面提到的默认采用异步方案之外还有以下几点。本仓库在 packages/koa 中基于 TypeScript 重新实现了 Koa 的核心对应eggjs/koa包包含 application.ts、context.ts、request.ts、response.ts 四个核心模块我们可以在阅读源码的过程中印证 Koa 的设计理念。中间件洋葱模型Koa 的中间件与 Express 不同Koa 采用洋葱模型onion model一次请求在单个中间件中会被执行两次进入时执行上游逻辑await next()之后执行下游逻辑。相较于 Express 中间件Koa 实现后置处理逻辑post-processing非常简单。对比 Koa 的koa-compress与 Express 的compression两个压缩中间件的实现可以直观感受到 Koa 中间件模型的优势压缩响应体这类响应之后的逻辑在 Koa 中只需放在await next()之后即可而在 Express 中则需要依赖res.on(finish)等额外钩子。从源码层面看本仓库 packages/koa/src/application.ts 中callback()方法使用koa-compose将中间件数组组合为单一处理函数随后在handleRequest中被调用callback(): (req: IncomingMessage, res: ServerResponse) Promisevoid { const fn compose(this.middleware); // ... const handleRequest (req: IncomingMessage, res: ServerResponse) { const ctx this.createContext(req, res); if (this.ctxStorage) { return this.ctxStorage.run(ctx, async () { return await this.handleRequest(ctx, fn); }); } return this.handleRequest(ctx, fn); }; return handleRequest; }值得注意的两点use()方法application.ts会显式拒绝 generator 函数抛出Support for generators was removed错误——Koa 2.x 及之后的模型只接受返回 Promise 的中间件每次请求会通过ctxStorage.run(ctx, ...)将 Context 放入AsyncLocalStorage并暴露currentContextgetterapplication.ts这使得在任意深度的异步调用链中都能取回当前请求上下文是请求级状态传递如日志、traceId的底层基础设施。Context请求级上下文对象与 Express 在一次 HTTP 请求中只有Request和Response两个对象不同Koa 多了一个Context 对象在 Koa 1 中是中间件里的this在 Koa 2 中是中间件函数的第一个参数。可以把一次请求中所有相关属性都挂载到这个对象上例如贯穿整个请求生命周期的 traceId之后任何位置都可以取用。相比 request 和 responseContext 的语义更完整。与此同时Request和Response都被挂载到了 Context 对象上。与 Express 一样这两个对象提供了大量便捷方法辅助开发例如get request.queryget request.hostnameset response.bodyset response.status本仓库 packages/koa/src/context.ts 中可以看到 Context 对 Request 的完整委托query、path、url、hostname、protocol、ip、ips、secure、accepts()、get()等而在 context.ts 中则委托了 Response 的body、status、type、length、etag、redirect()、set()等。这意味着开发者在控制器里既可以直接ctx.body hi也可以精确读取ctx.request.hostname两套对象 API 并存且互相补充。异常处理同步写法带来的巨大红利以同步方式书写异步代码的另一个巨大优势是异常处理变得非常简单遵循约定编写的代码抛出的所有异常都可以用try catch捕获。我们可以轻松编写一个自定义的异常处理中间件async function onerror(ctx, next) { try { await next(); } catch (err) { ctx.app.emit(error, err); ctx.body server error; ctx.status err.status || 500; } }将这个中间件放在所有其他中间件之前即可捕获其后同步或异步代码抛出的所有异常。这一思路在 Egg 生态中已被封装为更完善的开箱即用插件。本仓库的 plugins/onerror 插件在 lib/onerror.ts 中重写了app.context.onerror它会先this.app.emit(error, err, this)触发应用级错误事件再根据客户端Accept头在html/text/json/js之间选择响应格式生产环境下默认隐藏err.message细节仅返回对应的 HTTP 状态文案并支持redirect到全局错误页等高级配置。可见try catch捕获 统一错误响应正是 Koa 异步模型的红利所在。Egg 继承自 Koa如前面所述Koa 是一个出色的框架但对于构建企业级应用来说它还不够。Egg 围绕 Koa 构建在 Koa 模型的基础上做了进一步的增强。这一点在源码中有非常直观的体现本仓库 packages/core/src/egg.ts 中EggCore extends KoaApplication并直接 re-export 了KoaApplication、KoaContext、KoaRequest、KoaResponseegg.ts同时在其上扩展出Controller、Service、Loader、Router、Lifecycle等企业级能力而packages/egg又在EggCore之上构建了完整的应用层。扩展Extension增强四个 Koa 核心对象在基于 Egg 的框架或应用中可以通过定义app/extend/{application,context,request,response}.js来扩展 4 个 Koa 对象的原型从而快速编写更多工具方法。例如在app/extend/context.js中// app/extend/context.js module.exports { get isIOS() { const iosReg /iphone|ipad|ipod/i; return iosReg.test(this.get(user-agent)); }, };随后即可在控制器中使用// app/controller/home.js exports.handler (ctx) { ctx.body ctx.isIOS ? Your operating system is iOS. : Your operating system is not iOS.; };Egg 启动时会将该对象与 Koa 对应对象的原型合并生成基于扩展原型的app、ctx等实例。本仓库对此机制的实现非常典型框架自身就是通过扩展来增强四个对象的packages/egg/src/app/extend/context.ts在 Context 上扩展了cookies、httpclient/curl、router、helper、logger/coreLogger、locals/state、runInBackground、queries、realStatus等请求级能力其中runInBackground通过setImmediate将异步任务放到后台执行并对失败任务发出error事件适合响应已完成、后续异步收尾的场景packages/egg/src/app/extend/request.ts扩展了host、protocol、ips/ip、query/queries含key[]数组形式兼容、acceptJSON等请求解析能力并支持基于app.config.proxy从X-Forwarded-*头获取真实协议、主机与 IPpackages/egg/src/app/extend/response.ts扩展了realStatus用于记录发生 302 跳转到全局错误页时真实业务状态码仍为 500这类场景。建议遵循官方实践属性计算通常只需一次应使用Symbol Getter 缓存的方式避免重复计算损耗性能。更多扩展细节请参见 扩展Extend 一节。插件Plugin独立功能的强大封装众所周知Express 和 Koa 会引入大量中间件提供各种功能例如 koa-session 提供 Session 支持、koa-bodyparser 帮助解析请求体。Egg 提供了强大的插件机制让编写独立功能变得更加容易。一个插件可以包含extend扩展基础对象的上下文提供工具方法与属性middleware添加一个或多个中间件为请求提供前置或后置处理逻辑config在不同环境下配置默认值。一个独立的模块化插件可以在高可维护性的前提下提供丰富的功能。由于插件支持在不同环境下配置默认值使用者几乎可以忘记配置这件事。eggjs/security 中也内置声明了一批官方插件onerror、security、session、i18n、view等它们的完整实现位于 plugins 目录读者可以在plugins/onerror、plugins/security等包中对照学习extend middleware config三段式结构。关于为什么 Koa 已有中间件机制仍需要插件官方文档在 插件Plugin 中给出了明确理由中间件加载顺序完全依赖用户手动编排、中间件定位偏向请求拦截而难以承载定时任务/消息订阅等非请求逻辑、部分功能需要复杂的应用启动期初始化——这些都需要一个更强大的机制来管理与编排相对独立的业务逻辑。简言之插件是微缩版应用含 Service、middleware、config、extend但没有独立的 Router 与 Controller可以嵌套中间件多个插件还能组装成上层框架。路线图Egg 1.x 与 Egg 2.x 的异步方案选择Egg 1.xEgg 1.x 发布时Node.js LTS 版本尚不支持 async function因此Egg 1.x 基于 Koa 1.x。在此基础上Egg 增加了完整的 async function 支持并且完全兼容 Koa 2.x 的中间件所有应用都可以使用async function编写底层基于 Koa 1.x异步方案基于 co 包裹的 generator 函数官方插件与 Egg 核心使用 generator 函数编写保持对 Node.js LTS 版本的支持必要时使用 co 兼容 async function应用开发者可自由选择 async functionNode.js 8.x或 generator 函数Node.js 6.x。Egg 2.x当 Node.js 8 成为 LTS 版本后async function 可以在 Node.js 中无性能问题地使用。Egg 发布了基于Koa 2.x的 2.x 版本底层基于 Koa 2.x异步方案基于 async function官方插件与 Egg 核心均使用 async function 编写推荐用户将业务层迁移到 async function仅支持 Node.js 8。同时 Egg 2.x 依然兼容 generator 函数以及 Egg 1.x 的全部用法——基于 Egg 1.x 的应用只需升级到 Node.js 8 即可平滑迁移到 Egg 2.x。这一兼容策略从本仓库源码中亦可得到印证EggCore的use()方法packages/core/src/egg.ts在继承 Koause()的基础上重新实现仅校验函数类型、不拒绝 generatorKoa 层才拒绝为历史兼容留出了灵活性而 packages/koa/src/application.ts 的use()则严格执行 Koa 2.x 的 async-only 契约。这种核心走 Koa 2 纯异步模型、上层 Egg 保留兼容的分层设计正是从 1.x 到 2.x 平滑演进路线在代码层面的延续。小结从回调地狱到 async/await从 Express 线性中间件到 Koa 洋葱模型再到 Egg 的扩展与插件机制这条演进路径的核心诉求始终一致让异步代码可读、可维护让企业级功能可组装、可复用。理解 Egg 与 Koa 的继承关系EggCore extends KoaApplication掌握洋葱模型中间件与 Context 委托机制熟悉app/extend/*扩展与插件三段式结构是深入使用 Egg 构建生产级应用的基础。下一步可以继续阅读 扩展Extend 与 插件Plugin并结合本仓库 packages/egg/src/app/extend 与 plugins 下的真实实现进行实践验证。赞分享后端Web框架【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址https://gitcode.com/gh_mirrors/eg/egg点击查看免费下载相关推荐get-shit-done 贡献指南Issue-First 流程、Changeset 机制与 node:test 测试纪律get shit done 贡献指南Issue First 流程、Changeset 机制与 node:test 测试纪律 GSDget shit done后端Web框架如何通过 issue 表单提交外部插件进入 awesome-copilot 插件市场并通过质量门禁如何通过 issue 表单提交外部插件进入 awesome copilot 插件市场并通过质量门禁 如果你把自己的 Copilot 插件发布在 GitHub后端Web框架Egg 框架概览为企业级应用与框架而生的 Node.js 与 Koa 框架Egg 框架概览为企业级应用与框架而生的 Node.js 与 Koa 框架 EggEgg.js是一个定位独特的 Node.js Web 框架——它不只是帮后端Web框架上一篇30-seconds-of-python项目解析使用Python轻松实现日期加减操作下一篇深入解析AWS KMS Python SDK示例代码从入门到实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价