>_ DevTrendszh

语言

首页

语言

板块

前端 后端 移动端 DevOps AI / ML 游戏开发 区块链 嵌入式 安全
TypeScript

Cordis 和 TypeScript 中的插件架构:告别繁琐

如果你曾经在 Node.js 或 TypeScript 中编写过可扩展的应用,你可能遇到过同样的陷阱。用户或系统连接了一个插件。插件挂载了五个事件监听器,启动了几个定时器,注册了路由,并注入了一个服务。然后插件被禁用或实时更新了。

接下来会发生什么?没错,内存泄漏。监听器继续挂着,定时器在后台滴答作响,上下文引用阻止了垃圾回收器清理内存。在 Node.js 中,管理依赖的生命周期通常会变成手动繁琐的工作。

前些日子,Koishi 聊天机器人框架的开发者们正好遇到了这个问题。他们需要创建一个核心,让数百个第三方插件能够启动、隔离、互相替换,以及在不重启进程的情况下卸载。就这样,Cordis 框架诞生了。

Cordis 到底是什么

创建者称他们的项目为"时空可组合性的元框架"。听起来很聪明很自负,但本质实际上很接地气。

Cordis 结合了依赖注入容器(IoC)、事件总线和层级上下文树。每个插件或服务都生活在自己的上下文中。如果该上下文被销毁,Cordis 会自动清理它的所有资源:移除事件处理器、终止定时器,并删除创建的服务。

这里没有魔法,但有明确的规范:如果插件使用了 [object Object][object Object][object Object] 这些方法,框架会自行负责清理副作用。

上下文模型是如何工作的

库的中心概念是 [object Object]。它不仅仅是一个带有设置的扁平对象,而是一个分支树。

当你调用 [object Object] 时,框架会生成一个子上下文(fork)。子上下文继承父级的服务,但存储自己对注册资源的引用。

如果你禁用插件 A,它的子上下文就会崩溃。[object Object] 处理器会从共享事件总线上移除,而数据库服务和插件 B 会继续平静地工作。

TypeScript 中的服务和类型化

Cordis 中的服务通过继承基类 [object Object] 来声明。这使得它们可以通过上下文属性直接访问,同时保持严格的类型化:

[object Object] 这个构造解决了模块加载顺序的问题。如果数据库服务异步初始化或稍后连接,依赖的插件会等待其就绪后再激活自己。

微调作用域可见性

在实际的程序中,模块通常不应该对所有事情都做出反应。例如,一个处理器只需要处理来自特定频道的消息或带有特定头部的请求。

Cordis 通过 [object Object] 调用和上下文属性引入了过滤器的概念。你可以将服务的可见性限制在树的特定分支,或者设置一个谓词来过滤掉不需要的事件:

这种方案适合什么任务

这个库是为特定类别的应用而创建的。你不应该把它拖到使用 Fastify 或 Express 的常规 CRUD API 中,那样只会增加一层额外的抽象。

但 Cordis 非常适合以下场景:

  1. 模块化 CLI 工具和生成器。当用户可以交付扩展命令或构建流水线的 npm 包时。
  2. 基于 Electron/Tauri 的桌面应用。用于组织插件系统和主题,这些可以实时启用和禁用,而无需重新加载窗口。
  3. 机器人和集成中心。如果一个服务与十几个不同平台(Telegram、Discord、Slack)通信,每个适配器都需要独立运行。
  4. 自动化工具。用户通过 Web 界面或 YAML 文件动态配置流程的地方。

陷阱和缺点

没有完美的工具,Cordis 也有很多特定的细节:

  • 学习曲线陡峭。文档使用干巴巴的语言编写,充满了大量特定术语。要理解作用域合并和副作用的概念,你需要仔细阅读源代码。
  • API 不熟悉。通过 TypeScript 的类型合并模块将服务绑定到上下文对象,对于习惯使用 NestJS 或 InversifyJS 装饰器的人来说,起初可能会感到困惑。
  • 绑定于思维模型。如果你的项目架构不涉及频繁的动态代码卸载,内置效果管理器的好处会被代码复杂性所抵消。

值得一试吗

Cordis 是一个专注于组件生命周期管理的有趣工程项目。它优雅地解决了创建可扩展系统时的资源泄漏问题。

如果你正在设计一个具有发达的可插拔插件生态系统的系统,并且想要开箱即用的可靠副作用追踪机制,请 fork 仓库 [object Object] 并研究测试中的示例。这是一个很好的例子,展示了如何在纯 TypeScript 中构建微内核架构。

相关项目