跳到主要内容
版本:Next

从 v7 迁移

InversifyJS v8 从 v7 引入了一些重大更改。本指南将帮助你将现有的 InversifyJS v7 代码迁移到 v8。

更改概述

下表总结了从 v7 到 v8 的主要更改:

v7v8备注
container.load(...)container.loadAsync(...)异步 load 重命名为 loadAsync
container.loadSync(...)container.load(...)同步 load 成为默认的 load
container.unbind(...)container.unbindAsync(...)异步 unbind 重命名为 unbindAsync
container.unbindSync(...)container.unbind(...)同步 unbind 成为默认的 unbind
container.unbindAll()container.unbindAllAsync()异步 unbindAll 重命名为 unbindAllAsync
container.unbindAllSync()container.unbindAll()同步 unbindAll 成为默认的 unbindAll
container.rebind(...)container.rebindAsync(...)异步 rebind 重命名为 rebindAsync
container.rebindSync(...)container.rebind(...)同步 rebind 成为默认的 rebind
container.unload(...)container.unloadAsync(...)异步 unload 重命名为 unloadAsync
container.unloadSync(...)container.unload(...)同步 unload 成为默认的 unload
options.unbind(异步)options.unbindAsyncContainerModuleLoadOptions 异步 unbind 重命名
options.unbindSyncoptions.unbindContainerModuleLoadOptions 同步 unbind 成为默认
options.rebind(异步)options.rebindAsyncContainerModuleLoadOptions 异步 rebind 重命名
options.rebindSyncoptions.rebindContainerModuleLoadOptions 同步 rebind 成为默认
ServiceIdentifier 接受 FunctionServiceIdentifier 要求 NewableAbstractNewable更严格的类服务标识符类型
.toProvider(...).toFactory(...) 与异步函数Provider 类型和 toProvider 绑定已删除;请改用 toFactory
CJS/ESM 双包仅 ESM包现在仅支持 ESM;Node.js LTS 支持 require(esm)

包格式

InversifyJS v8 现在是一个仅支持 ESM 的包。对于大多数用户来说,这不是一个破坏性更改:所有活跃的 Node.js LTS 版本(20.x 及以上)都支持 require(esm),这意味着 CommonJS 项目仍然可以在不做任何更改的情况下使用该包。

有关 CJS → ESM 迁移的更广泛概述,请参阅 将生态系统迁移到 ES 模块 博客文章。

容器 API

方法重命名:同步优先约定

v8 中最重大的更改是采用了同步优先命名约定。在 v7 中,默认方法名称(例如 loadunbindrebindunload)是异步的并返回 Promise,而同步变体带有 Sync 后缀。在 v8 中,这被反转了:默认名称现在是同步的,而异步变体带有 Async 后缀。

load / loadAsync

在 v7 中,container.load 是异步的。在 v8 中,同步版本成为默认:

v7:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana).whenNamed('Melee');
},
);

// v7: async load (default)
await container.load(weaponsModule);

container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

v8:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana).whenNamed('Melee');
},
);

// v8: sync load (default)
container.load(weaponsModule);

container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

unbind / unbindAsync

在 v7 中,container.unbindSync 是同步变体。在 v8 中,它成为默认的 unbind

v7:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v7: sync unbind
container.unbindSync('Weapon');

const isBound: boolean = container.isBound('Weapon');

v8:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v8: sync unbind (default)
container.unbind('Weapon');

const isBound: boolean = container.isBound('Weapon');

unbindAll / unbindAllAsync

在 v7 中,container.unbindAllSync 是同步变体。在 v8 中,它成为默认的 unbindAll

v7:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v7: sync unbindAll
container.unbindAllSync();

const isBound: boolean = container.isBound('Weapon');

v8:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v8: sync unbindAll (default)
container.unbindAll();

const isBound: boolean = container.isBound('Weapon');

rebind / rebindAsync

在 v7 中,container.rebindSync 是同步变体。在 v8 中,它成为默认的 rebind

v7:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v7: sync rebind
container.rebindSync<Weapon>('Weapon').to(Shuriken);

const weapon: Weapon = container.get<Weapon>('Weapon');

v8:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v8: sync rebind (default)
container.rebind<Weapon>('Weapon').to(Shuriken);

const weapon: Weapon = container.get<Weapon>('Weapon');

unload / unloadAsync

在 v7 中,container.unloadSync 是同步变体。在 v8 中,它成为默认的 unload

v7:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana);
},
);

await container.load(weaponsModule);

// v7: sync unload
container.unloadSync(weaponsModule);

const isBound: boolean = container.isBound('Weapon');

v8:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana);
},
);

container.load(weaponsModule);

// v8: sync unload (default)
container.unload(weaponsModule);

export const isBound: boolean = container.isBound('Weapon');
提示

如果你的代码库主要使用同步变体(例如 loadSyncunbindSyncrebindSyncunloadSync),迁移很简单:只需删除 Sync 后缀。

如果你主要使用异步变体,请通过添加 Async 后缀来重命名它们(例如 loadloadAsyncunbindunbindAsync)。

ContainerModuleLoadOptions

相同的同步优先约定也适用于 ContainerModuleLoadOptions

v7:

const module: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
// v7: sync unbind and rebind
options.unbindSync('Weapon');
options.bind<Weapon>('Weapon').to(Shuriken);
},
);

await container.load(module);

v8:

const module: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
// v8: sync unbind and rebind (default)
options.unbind('Weapon');
options.bind<Weapon>('Weapon').to(Shuriken);
},
);

container.load(module);

ServiceIdentifier 类型

在 v7 中,ServiceIdentifier 接受任何 Function 作为有效的服务标识符。在 v8 中,类型变得更严格:Function 仅在满足 Newable<T>AbstractNewable<T> 时才有效。这提供了更好的类型安全性,并确保只有可构造的类才能用作基于类的服务标识符。

// v7 — 接受任何函数
type ServiceIdentifier<T> = string | symbol | Newable<T> | Function;

// v8 — 仅接受可构造的类
type ServiceIdentifier<T> = string | symbol | Newable<T> | AbstractNewable<T>;

在实践中,此更改对大多数代码库是透明的,因为服务标识符通常是类、字符串或符号。如果你使用普通函数作为服务标识符,则需要改用字符串或符号标识符。

已删除的 toProvider 绑定和 Provider 类型

toProvider 绑定方法和 Provider 类型已在 v8 中删除。提供者是返回 Promise<T> 的工厂,因此可以使用 toFactory 与异步函数实现相同的行为:

v7:

export const container: Container = new Container();

container.bind<Sword>('Sword').to(Katana);

// v7: toProvider (deprecated)
container
.bind<SwordProvider>('SwordProvider')
.toProvider((context: ResolutionContext) => {
return async (material: string, damage: number): Promise<Sword> => {
return new Promise<Sword>(
(resolve: (value: Sword | PromiseLike<Sword>) => void) => {
setTimeout(() => {
const sword: Sword = context.get<Sword>('Sword');
sword.material = material;
sword.damage = damage;
resolve(sword);
}, 10);
},
);
};
});

v8:

export const container: Container = new Container();

container.bind<Sword>('Sword').to(Katana);

// v8: use toFactory with async function instead of toProvider
container
.bind<SwordProvider>('SwordProvider')
.toFactory((context: ResolutionContext) => {
return async (material: string, damage: number): Promise<Sword> => {
return new Promise<Sword>(
(resolve: (value: Sword | PromiseLike<Sword>) => void) => {
setTimeout(() => {
const sword: Sword = context.get<Sword>('Sword');
sword.material = material;
sword.damage = damage;
resolve(sword);
}, 10);
},
);
};
});

快速迁移清单

  1. 更新异步方法调用:在使用异步版本的地方,将 loadloadAsyncunbindunbindAsyncrebindrebindAsyncunloadunloadAsyncunbindAllunbindAllAsync 进行重命名。
  2. 更新同步方法调用:从 loadSyncunbindSyncrebindSyncunloadSyncunbindAllSync 中删除 Sync 后缀。
  3. 更新 ContainerModuleLoadOptions 用法:对 unbind/unbindSyncunbindAsync/unbindrebind/rebindSyncrebindAsync/rebind 应用相同的重命名。
  4. toProvider 替换为 toFactory:改用 toFactory 与异步函数。
  5. 删除 Provider 类型导入:替换为自定义类型或改用 Factory
  6. 检查 ServiceIdentifier 用法:确保你没有使用普通函数作为服务标识符。
  7. 验证 ESM 兼容性:确保你的项目可以使用 ESM 包(Node.js 20.x+ 支持 require(esm))。