Skip to content

从 0.1 迁移到 0.2

0.2 是以“显式资源所有权”为核心的重构:Container 与 Proxy 的动态派发被替换为显式的作用域树,所有清理路径可等待。本文列出面向插件与服务作者的破坏性变更与迁移方法。

Container 移除

Container 已从 0.2 移除,作用域(Scope)是唯一的资源管理入口。API 对照:

Container(0.1)Scope(0.2)说明
register(name, target)scope.register(name, target)惰性注册,语义一致
get(name)scope.get(name)未命中返回 undefined,不再抛错
has(name)scope.has(name)沿父链查找
list()scope.list()含父链
scope.set(name, value)纯登记,无实例化
scope.provide(name, svc)start/stop 生命周期管理

自行装配框架的脚本需要从:

typescript
const container = new Container();
injectionProvider(container, config, { headless: false });
const app = container.get('app');

迁移为:

typescript
const rootScope = createScope();
injectionProvider(rootScope, config, { headless: false });
const app = rootScope.get('app');

服务即插件

0.1 的独立服务装载路径(service.files / service.scopes + { name, default: 类或工厂 } 模块)已废弃。服务以插件形态编写,在主体内通过 context.share() 提升到根作用域:

typescript
// 0.1:service.files 装载的 { name, default } 服务模块
export const name = 'my-service';
export default class MyService {
    static inject = ['logger'] as const;
    constructor(private logger: LoggerService) {}
}

// 0.2:plugin.files 装载的服务插件
export const inject = ['logger'];
export const name = 'my-service';
export default async function (context: Context) {
    await context.share(new MyService(context.get('logger')!)); // 缺省服务名 = 插件名
}

要点:

  • 服务插件经 plugin.files / 插件依赖扫描装载,saukko plugin enable / disable 直接管理;
  • share 的服务随插件启用而启动、随禁用而从根作用域摘除、随卸载而清理(详见 服务生命周期);
  • 依赖该服务的插件在服务消失时自动级联停止,服务恢复后自动重启——不再需要手动处理顺序;
  • ServiceRegistry 声明扩展机制不变,消费方照常 inject + context.get()

插件清理改为可等待的 Lifecycle 钩子

0.1 的内部事件 internal.ready / internal.dispose 已移除。清理逻辑迁移到 context.lifecycle

typescript
// 0.1
context.on('internal.dispose', async () => { await cleanup(); });

// 0.2
context.lifecycle.onStop(async () => { await cleanup(); });

要点:

  • 钩子持久保留:onStop 在每次 disable 时执行,onStart 在每次 enable 时执行;onDispose 是仅卸载时执行一次的终态钩子;
  • onStop 可注册多次,按注册逆序执行;onBeforeStop 早于 onStop
  • 插件禁用、卸载、应用停止都会等待清理完成;单个插件清理失败不阻断其余插件;
  • 事件监听(context.on)在卸载时自动移除,无需手动 off;disabled 期间监听被门控,重新启用后自动恢复。

install 即执行,enable/disable 是纯开关

插件主体的执行时机从"启用时"提前到"安装时":

操作0.10.2
install仅登记依赖就绪即执行主体;未就绪则挂起等待
enable执行主体仅触发 onStart 钩子;依赖缺失时标记期望启用
disable销毁上下文仅触发 onStop 钩子,子作用域保留
uninstall终态销毁子作用域并移除

迁移注意:主体中"只需执行一次"的初始化(注册监听、share 服务)保持在主体内;"每次启用都要做"的逻辑(连接、定时器)移入 onStart,对应清理移入 onStop

依赖声明的变化

  • inject 中引用其他插件:仅约束启动顺序,不做实例注入;0.1 中此类依赖会被误判为缺失导致插件无法启用,0.2 已修正;
  • 缺失依赖从"拒绝启用"改为等待语义:install 挂起主体、enable 标记期望启用,依赖补齐后自动执行并启动;循环依赖仍是硬错误;
  • 依赖消失(服务插件被禁用/卸载)时依赖方自动级联停止,恢复后自动重启;
  • 上下文读取:0.1 的 context.dependencies 仍兼容保留(为安装时的快照),推荐改用 context.get()——沿父链动态解析,服务替换后读到的始终是最新实例;未声明 inject 的服务也可读取。

多形态插件入口

程序化 install 新支持函数、类、含 apply 方法的对象三种形态;{ name, default } 模块形态不变,仍是包加载的唯一形态。详见 插件形态

行为变化速查

  • App.stop() 按启动拓扑的逆序卸载全部插件(0.1 为安装顺序停止);
  • 插件安装失败(主体抛错)会清理其子作用域且不留记录,可直接重新 install;
  • 插件启用失败(onStart 抛错)进入 FAILED 并保持未启用,可直接重试 enable;
  • uninstall 清理失败时插件保持已禁用状态,可重试卸载;
  • Context.get 未命中返回 undefined 而非抛错;
  • plugin list 会展示每个插件的状态:enabled / disabled / waiting: <依赖清单>

Saukko.js 系列项目均使用 Apache 2.0 协议开源。