🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## Typecript的用法 使用TypeDI,你可以使用一个命名的服务。比如: ``` import { Container, Service, Inject } from 'typedi'; interface Factory { create(): void; } @Service({ id: 'bean.factory' }) class BeanFactory implements Factory { create() {} } @Service({ id: 'sugar.factory' }) class SugarFactory implements Factory { create() {} } @Service({ id: 'water.factory' }) class WaterFactory implements Factory { create() {} } @Service({ id: 'coffee.maker' }) class CoffeeMaker { beanFactory: Factory; sugarFactory: Factory; @Inject('water.factory') waterFactory: Factory; constructor(@Inject('bean.factory') beanFactory: BeanFactory, @Inject('sugar.factory') sugarFactory: SugarFactory) { this.beanFactory = beanFactory; this.sugarFactory = sugarFactory; } make() { this.beanFactory.create(); this.sugarFactory.create(); this.waterFactory.create(); } } let coffeeMaker = Container.get<CoffeeMaker>('coffee.maker'); coffeeMaker.make(); ``` 如果你想存储(并在以后注入)一些设置或配置选项,这个功能特别有用。比如说: ``` import { Container, Service, Inject } from 'typedi'; // 在你的全局应用参数中的某个地方 Container.set('authorization-token', 'RVT9rVjSVN'); @Service() class UserRepository { @Inject('authorization-token') authorizationToken: string; } ``` 当你写测试时,你可以很容易地使用`set`方法为你测试的类提供你自己的 "假 "依赖:`provide `容器的方法。 ``` Container.set(CoffeeMaker, new FakeCoffeeMaker()); // 或者对于命名的服务 Container.set([ { id: 'bean.factory', value: new FakeBeanFactory() }, { id: 'sugar.factory', value: new FakeSugarFactory() }, { id: 'water.factory', value: new FakeWaterFactory() }, ]); ``` ***** ## TypeScript 高级使用范例 * [使用工厂函数创建服务 ](#使用工厂函数创建服务) * [使用工厂类来创建服务 ](#使用工厂类来创建服务) * [循环引用的问题](#循环引用的问题) * [自定义装饰器](#自定义装饰器) * [使用服务组 ](#使用服务组 ) * [使用多个容器和作用域容器 ](#使用多个容器和作用域容器 ) * [移除已注册的服务或重置容器状态](#移除已注册的服务或重置容器状态) ### 使用工厂函数创建服务 你可以使用工厂函数在容器中创建你的服务。 这样,服务实例将通过调用你的工厂函数而不是直接实例化一个类来创建。 ``` import { Container, Service } from 'typedi'; function createCar() { return new Car('V8'); } @Service({ factory: createCar }) class Car { constructor(public engineType: string) {} } // 从容器中获取服务 // 服务将通过调用指定的工厂函数来创建 const car = Container.get(Car); console.log(car.engineType); // > "V8" ``` ### 使用工厂类来创建服务 你也可以使用工厂类来创建你的服务。 这样,服务实例将通过调用给定的工厂服务的方法工厂来创建,而不是直接实例化一个类 ``` import { Container, Service } from 'typedi'; @Service() class CarFactory { constructor(public logger: LoggerService) {} create() { return new Car('BMW', this.logger); } } @Service({ factory: [CarFactory, 'create'] }) class Car { constructor(public model: string, public logger: LoggerInterface) {} } ``` ### 循环引用的问题 在语言中,有一个已知的问题,它不能处理循环引用。比如说: ``` // Car.ts @Service() export class Car { @Inject() engine: Engine; } // Engine.ts @Service() export class Engine { @Inject() car: Car; } ``` 这段代码不会工作,因为Engine有一个对Car的引用,而Car有一个对Engine的引用。其中一个将是未定义的,这会导致错误。为了解决这个问题,你需要在一个函数中这样指定一个类型。 ``` // Car.ts @Service() export class Car { @Inject(type => Engine) engine: Engine; } // Engine.ts @Service() export class Engine { @Inject(type => Car) car: Car; } ``` 就这样了。构造函数的注入也是如此。 ### 自定义装饰器 你可以创建你自己的装饰器,它将为你的服务依赖性注入你给定的值。比如说: ``` // LoggerInterface.ts export interface LoggerInterface { log(message: string): void; } // ConsoleLogger.ts import { LoggerInterface } from './LoggerInterface'; export class ConsoleLogger implements LoggerInterface { log(message: string) { console.log(message); } } // Logger.ts export function Logger() { return function (object: Object, propertyName: string, index?: number) { const logger = new ConsoleLogger(); Container.registerHandler({ object, propertyName, index, value: containerInstance => logger }); }; } // UserRepository.ts @Service() export class UserRepository { constructor(@Logger() private logger: LoggerInterface) {} save(user: User) { this.logger.log(`user ${user.firstName} ${user.secondName} has been saved.`); } } ``` ### 使用服务组 你可以将多个服务分组到一个以服务ID或token标记的组中。比如说: ``` // Factory.ts export interface Factory { create(): any; } // FactoryToken.ts export const FactoryToken = new Token<Factory>('factories'); // BeanFactory.ts @Service({ id: FactoryToken, multiple: true }) export class BeanFactory implements Factory { create() { console.log('bean created'); } } // SugarFactory.ts @Service({ id: FactoryToken, multiple: true }) export class SugarFactory implements Factory { create() { console.log('sugar created'); } } // WaterFactory.ts @Service({ id: FactoryToken, multiple: true }) export class WaterFactory implements Factory { create() { console.log('water created'); } } // app.ts // 现在你可以在一个数组中获得所有的工厂了 Container.import([BeanFactory, SugarFactory, WaterFactory]); const factories = Container.getMany(FactoryToken); // factories是Factory[] factories.forEach(factory => factory.create()); ``` ### 使用多个容器和作用域容器 默认情况下,所有的服务都存储在全局服务容器中,这个全局服务容器持有你的每个服务的所有唯一实例。 如果你想让你的服务根据某些用户上下文(例如http请求)以不同的方式行事并在里面存储数据--你可以为不同的上下文使用不同的容器。比如说: ``` // QuestionController.ts @Service() export class QuestionController { constructor(protected questionRepository: QuestionRepository) {} save() { this.questionRepository.save(); } } // QuestionRepository.ts @Service() export class QuestionRepository { save() {} } // app.ts const request1 = { param: 'question1' }; const controller1 = Container.of(request1).get(QuestionController); controller1.save('Timber'); Container.reset(request1); const request2 = { param: 'question2' }; const controller2 = Container.of(request2).get(QuestionController); controller2.save(''); Container.reset(request2); ``` 在这个例子中,`controller1`和`controller2`是完全不同的实例,这些控制器中使用的`QuestionRepository`也是不同的实例。 `Container.reset`删除了带有给定上下文标识符的容器。如果你想让你的服务完全是全局的,而不是针对容器的,你可以把它们标记为全局的。 ``` @Service({ global: true }) export class QuestionUtils {} ``` 而这个全局服务将是所有容器中的同一个实例。 TypeDI也支持函数依赖性注入。下面是例子: ``` export const PostRepository = Service(() => ({ getName() { return 'hello from post repository'; }, })); export const PostManager = Service(() => ({ getId() { return 'some post id'; }, })); export class PostQueryBuilder { build() { return 'SUPER * QUERY'; } } export const PostController = Service( [PostManager, PostRepository, PostQueryBuilder], (manager, repository, queryBuilder) => { return { id: manager.getId(), name: repository.getName(), query: queryBuilder.build(), }; } ); const postController = Container.get(PostController); console.log(postController); ``` ### 删除注册的服务或重置容器状态 如果你需要从容器中移除注册的服务,只需使用`Container.remove(...)`方法。你也可以通过调用`Container.reset()`方法完全重置容器。这将有效地从容器中删除所有的注册服务。