如何在TypeScript的tsyringe中配置DI容器,使Foo复用FooBar的Bar实例?
解决tsyringe中FooBar与内部Foo共享同一Bar实例的问题
你的问题核心是要让每个FooBar实例拥有专属的Bar实例,并且该FooBar内部的Foo也共享这个Bar实例——而当前代码中tsyringe会分别为FooBar和Foo创建独立的Bar实例,导致断言失败。
下面提供两种简洁的解决方案,适配不同的场景:
方法1:使用容器作用域(Container Scoped)+ 子容器
这是最贴合DI设计思想的方案,通过ContainerScoped生命周期和子容器,为每个FooBar实例创建独立的上下文,确保同一上下文内的Bar实例唯一:
import { injectable, container, Lifecycle, scoped } from "tsyringe"; it("injects one Bar instance per FooBar", () => { // 标记Bar为容器作用域:每个容器实例仅创建一个Bar @scoped(Lifecycle.ContainerScoped) class Bar { constructor() {} x = 1; } @injectable() class Foo { constructor(public myBar: Bar) {} } @injectable() class FooBar { constructor(public bar: Bar, public foo: Foo) {} } // 为每个FooBar创建独立的子容器 const childContainer = container.createChildContainer(); const fooBar = childContainer.resolve(FooBar); fooBar.bar.x = 2; expect(fooBar.foo.myBar.x).toBe(2); // 断言通过 });
原理说明
Lifecycle.ContainerScoped会让tsyringe在同一个容器实例中只创建一个Bar实例。我们为每个FooBar创建子容器后,resolve出的FooBar和它内部的Foo都会从这个子容器获取Bar,自然共享同一实例。
方法2:工厂注入(手动控制实例传递)
如果不想引入子容器,可以通过注册工厂函数,让FooBar自己创建Bar实例并传递给Foo,实现实例共享:
import { injectable, container, inject } from "tsyringe"; it("injects one Bar instance per FooBar via factory", () => { @injectable() class Bar { constructor() {} x = 1; } @injectable() class Foo { constructor(public myBar: Bar) {} } @injectable() class FooBar { public bar: Bar; public foo: Foo; // 注入Foo的工厂函数,接收Bar实例并返回Foo constructor(@inject("FooFactory") fooFactory: (bar: Bar) => Foo) { this.bar = new Bar(); // 创建当前FooBar专属的Bar this.foo = fooFactory(this.bar); // 将Bar传递给Foo } } // 注册Foo的工厂函数 container.register("FooFactory", { useFactory: () => (bar: Bar) => new Foo(bar) }); const fooBar = container.resolve(FooBar); fooBar.bar.x = 2; expect(fooBar.foo.myBar.x).toBe(2); // 断言通过 });
适用场景
当你需要更精细地控制实例的创建时机,或者不想依赖容器作用域时,这种方式更灵活。
内容的提问来源于stack exchange,提问作者Obiwahn
相关产品推荐
相关产品推荐

