第 2 章:Cordis 插件、依赖注入与生命周期
1. 本章定位
Cordis 是 Harness 的组合语法:服务、事件、能力贡献和 UI 插槽都随作用域创建与撤销。
2. 学习目标
- 区分函数插件、服务定义(Service Definition)、提供者与消费者。
- 解释依赖注入(Dependency Injection)和作用域(Scope)。
- 用副作用/释放器(Effect/Disposer)证明插件可卸载。
- 区分
emit、serial、parallel、waterfall。
3. 前置知识
可把 Context 类比为带生命周期的前端依赖容器,把 ctx.effect() 类比为 useEffect 的 cleanup。局限是 Cordis 还管理依赖等待、子作用域、typed event 和插件 fiber,不等于 React Context。
4. 对应源码
docs/cordis-primer.mddocs/cordis-tutorial/01-first-plugin.md至07-into-the-harness.mdpackages/core/agent-loop/src/index.ts:353:static inject。packages/core/agent-loop/src/index.ts:412:可撤销 factory 注册。
5. 工作原理
函数插件导出 apply(ctx, config);服务插件先定义 ctx.<key> 的类型和契约,再由 provider 注册实现,consumer 用 inject 声明运行时依赖。依赖未满足时插件等待;满足后在自己的 Scope 激活。所有监听器、服务和注册都应产生 Disposer,卸载时反向清理。
6. 执行流程
无图回退:Loader 创建插件 Scope → 检查 inject → 等待依赖或调用 apply() → 注册 service/listener/effect → 正常运行 → dispose → 反向撤销副作用。
7. 关键源码讲解
AgentLoop 是真实样例:packages/core/agent-loop/src/index.ts:353 声明所需服务,packages/core/agent-loop/src/index.ts:412 通过 effect 注册 Agent factory,packages/core/agent-loop/src/index.ts:416 注册系统提示词变量。重点不是某个 Map,而是谁拥有注册、何时可见、卸载后是否完全消失。
8. 调试与观察方法
在插件 apply()、依赖就绪回调和 Disposer 处断点;观察 Scope、服务键、监听器顺序。常见故障是 inject 键拼错、waterfall 忘记 next()、异步释放未完成。
9. 本章实践任务
实现 greeter provider 和 consumer:先缺省 provider 证明 consumer 等待,再挂载并调用,最后卸载两次确认没有残留监听器。验收:不使用全局单例,重复挂载结果一致。
10. 常见误区
inject不只是类型提示,它决定激活时机。waterfall不调用next()会有意或无意地短路。- 在插件作用域外注册会失去自动清理。
11. 自测题
- Service Definition、provider、consumer 各负责什么?
- Scope 为什么比全局容器安全?
- Effect 的 Disposer 何时运行?
serial与parallel如何选择?- 如何证明插件卸载没有资源泄漏?