第 3 卷
复制代码 1// service.ts 里 Service 构造函数的关键一行(节选)
2self.ctx.reflect.provide(name, self, this[symbols.check])
它把 this(这个服务实例)注册进上下文——你平时写的 ctx.provide(...) 本质上就是转发给 reflect.provide(...) 的。而且,reflect.provide 在注册之后、以及撤
销(返回的清理函数)时,都会调用 notify() 去通知所有依赖方。
前三步是“停”和“起”共用,我们先看这三步:
- notify 找到受影响的插件:
复制代码
| 1 | // reflect.ts里notify()的核心(节选,去掉了filter过滤与返回值收集) |
|---|---|
| 2 | notify(names) { |
| 3 | for(construntimeofthis.ctx.registry.values()) { |
| 4 | for(constfiberofruntime.fibers) { |
| 5 | lethasUpdate =false |
| 6 | for(constnameofnames) { |
| 7 | if(!(nameinfiber.inject))continue //没声明这个依赖→跳过 |
| 8 | hasUpdate =true |
| 9 | fiber._checkImpl(name) //重新检查:服务还在吗? |
| 10 | } |
| 11 | if(!hasUpdate)continue //一个依赖都没变→这个fiber不用动 |
| 12 | fiber._refresh() //重新计算依赖指纹 |
| 13 | } |
| 14 | } |
| 15 | } |
它遍历所有插件,只挑出「inject 里声明了 greeter 的那些 fiber:对每个声明了 greeter 的 fiber 做 _checkImpl,最后只要确实有依赖变化被查到,就做一次 _refresh。
复制代码 1// fiber.ts 里 _checkImpl 的核心(节选)
2_checkImpl(name) {
| 3 | constimpl =this.ctx.reflect._getImpl(name,true) | //去store重新查:greeter还 |
|---|---|---|
| 4 | if(!impl)returndeletethis._store[name] | //查不到了→把fiber自己记的 |
| 5 | this._store[name] = impl | //还查得到→更新记录 |
| 6 | } |
每个 fiber 都维护着一份 _store,记着“我依赖的那些服务的当前实例”。_checkImpl 做的就是把这份记录刷新一遍:服务没了就删掉,还在就更新成最新实例。
- _refresh 重算“依赖指纹”(node_modules/@deepseek-
ai/cordis/src/fiber.ts):
复制代码 1// fiber.ts 里 _refresh 的核心(节选)
2_refresh() { 3let epoch = ''
| 4 | for(constnameofObject.keys(this.inject)) { |
|---|---|
| 5 | constimpl =this._store[name] |
| 6 | if(!impl) { epoch =INACTIVE;break} //有依赖缺失了→标记为「不活跃」 |
| 7 | epoch +=':'+ impl.fiber.uid //依赖齐全→拼出它们的uid指纹 |
| 8 | } |
| 9 | this._setEpoch(epoch) |
| 10 | } |
它把“当前依赖的那些服务的 uid”拼成一个字符串指纹(epoch)。依赖齐全,指纹是一串 uid;缺了哪个,指纹直接变成 INACTIVE。
- 是分叉点——_setEpoch 拿新指纹和旧指纹一比,决定走哪条路
(node_modules/@deepseek-ai/cordis/src/fiber.ts):
3const oldEpoch = this._runner.epoch
| 4 | if(epoch === oldEpoch)return | //指纹没变→什么都不做(论文说的「无关 |
|---|---|---|
| 5 | this._runner.epoch= epoch | |
| 6 | if(epoch !==INACTIVE&& oldEpoch ===INACTIVE) { | |
| 7 | this._reload() | //指纹从「无」变「有」→插件起 |
| 8 | }else{ | |
| 9 | this._unload() | //指纹从「有」变「无」→插件停 |
| 10 | } | |
| 11 | } |
这就是“响应式”最核心的决策点:用依赖指纹有没有变、往哪个方向变,决定插件是停、是起、还是不动。它正好对应论文里说的三种情况——激活(activating,从无到有)、停用
(deactivating,从有到无)、无关(neutral,没变)。
下面我们来看两个分支各自的实现:
分支 A:_unload —— 插件停。_unload() 内部就是第一讲见过的那句 Promise.all(this._disposables.clear()...),把 fiber 上注册的所有 effect 一次
性清空。所以“停”的实现,就是复用第一讲的注册即副作用 + 倒序回收,consumer 当初注册的所有东⻄,在这一刻被逆序清掉。
分支 B:_reload —— 插件起。
复制代码 1// fiber.ts 里 _reload 的核心(节选)
2private async _reload() {
| 3 | this.store= { ...this._store} | //①用最新检查到的依赖,重建store |
|---|---|---|
| 4 | ... | |
| 5 | this.config=this._resolveConfig(this._config) | //②重新解析配置 |
| 6 | awaitthis._execute(this._runner) | //③★重新执行插件的apply! |
| 7 | } |