第 4 章 动手实战(一):第一个插件与生命周期
本章开始跟随官方 Cordis 教程动手实验。准备工作:
每一章都从这个目录运行同一条命令:
这个单文件启动器会创建根 Context、挂载 Loader 插件,并让它从当前目录加载 ./cordis.yml。--import tsx 让 Node 无需构建即可运行 TypeScript。
4.1 第一个插件
创建 hello.ts:
创建 cordis.yml:
运行:
背后发生了什么:
启动器创建根
Context,挂载 Loader 插件;Loader 读取
cordis.yml,解析./hello.ts,将其作为子插件挂载;Cordis 调用你的
apply(ctx);没有任何东西继续运行时,进程自行退出。
你的文件里没有框架启动代码:插件描述自己的贡献,cordis.yml 负责组合应用。官方的 dsh-base 就是一份更长的插件组合,由部署 overlay 对它进行修补。
注意:配置项会并发启动,列表位置不保证加载先后;顺序由 inject 服务依赖决定。
试错:让 apply 抛异常
再次运行,进程会因该错误而终止——插件加载失败会明确报错,不会静默跳过。
一个例外要尽早知道:如果配置项的模块无法被解析(路径或包名拼写错误),Cordis 会通过 logger 服务报告错误而不崩溃,且启动早期这条报告可能在 console 导出器开始观察之前丢失。新增配置项没效果时,先检查拼写。
4.2 生命周期与 Effect
Cordis 插件可能因修改配置、热重载、显式释放或所需服务消失而卸载。通过 Cordis API 建立的注册属于 effect,会随插件卸载自动撤销;在这些 API 之外管理的资源必须包装在 ctx.effect() 中。
创建 lifecycle.ts:
cordis.yml 改为 - name: './lifecycle.ts',运行输出:
三个要点:
ctx.plugin(heartbeat)把一个来自代码的函数挂载为插件——与 YAML loader 做的一样。函数插件不需要apply方法(只有对象形态才要求)。调用返回一个 fiber:已加载插件实例的运行时句柄。effect 主体在加载期间运行,返回的 disposer 在卸载期间运行。生命周期与插件一致的资源,永远不需要你手动调用 disposer。
fiber.dispose()会等该插件所有清理工作(含异步 disposer)完成后才结束,并递归卸载它挂载的所有子插件。
Fiber 状态机
| 状态 | 含义 |
|---|---|
| PENDING | 已声明,但所需服务尚不可用 |
| LOADING / ACTIVE | apply 正在运行 / 已完成 |
| FAILED | apply 或配置校验抛出异常 |
| UNLOADING / DISPOSED | disposer 正在运行 / 一切已拆除 |
PENDING 是"为什么我的插件没有输出"的最常见答案(第 7 章详解诊断方法)。
哪些操作已经是 effect
你很少需要手写 ctx.effect(),因为内置注册 API 本身就是 effect:
ctx.on(event, listener):监听器随插件卸载而移除;ctx.plugin(child):子插件随父插件一同 dispose;服务注册与 harness 注册表(如
ctx.tools.register(...))会把返回的 disposer 附着到调用插件上,自动撤销。
释放顺序注意事项
disposer 按注册顺序的逆序启动,但多个异步 disposer 会并发运行。若拆除步骤必须按顺序执行,把它们放进同一个 disposer 里依次 await。
依据官方教程第 1、2 章(01-first-plugin、02-lifecycle-and-effects)整理。