{"id":13526755,"url":"https://github.com/opensumi/di","last_synced_at":"2025-04-09T21:18:29.927Z","repository":{"id":36961144,"uuid":"415831406","full_name":"opensumi/di","owner":"opensumi","description":"A Dependency Injection Library for JavaScript. Support AOP.","archived":false,"fork":false,"pushed_at":"2024-09-23T11:51:37.000Z","size":259,"stargazers_count":69,"open_issues_count":1,"forks_count":13,"subscribers_count":21,"default_branch":"main","last_synced_at":"2025-04-09T21:18:24.436Z","etag":null,"topics":["browser","dependency-injection","ioc","nodejs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opensumi.png","metadata":{"files":{"readme":"README-zh_CN.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-11T07:59:06.000Z","updated_at":"2025-03-23T05:40:41.000Z","dependencies_parsed_at":"2024-05-31T11:12:59.217Z","dependency_job_id":null,"html_url":"https://github.com/opensumi/di","commit_stats":{"total_commits":165,"total_committers":7,"mean_commits":"23.571428571428573","dds":0.5212121212121212,"last_synced_commit":"1b74a8709dadb321cbaac0289c8375ce137bccba"},"previous_names":["ide-framework/common-di"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensumi%2Fdi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensumi%2Fdi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensumi%2Fdi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensumi%2Fdi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opensumi","download_url":"https://codeload.github.com/opensumi/di/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248111973,"owners_count":21049578,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","dependency-injection","ioc","nodejs"],"created_at":"2024-08-01T06:01:34.247Z","updated_at":"2025-04-09T21:18:29.894Z","avatar_url":"https://github.com/opensumi.png","language":"TypeScript","readme":"# @opensumi/di\n\n这个工具将会帮助你很好的实现依赖反转，而不用关注那些对象实例化的细节。同时，因为对象的实例化在注册器中进行创建，所以工厂模式和单例模式都很容易实现。\n\n## Table of Contents\n\n- [Install](#install)\n- [Quick Start](#quick-start)\n- [API](#api)\n- [Examples](#examples)\n- [FAQ](#faq)\n- [Related Efforts](#related-efforts)\n\n## Install\n\n```sh\nnpm install @opensumi/di --save\nyarn add @opensumi/di\n```\n\n将您的 tsconfig.json 修改为包含以下设置：\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true\n  }\n}\n```\n\n为 Reflect API 添加一个 polyfill（下面的例子使用 reflect-metadata）。你可以使用：\n\n- [reflect-metadata](https://www.npmjs.com/package/reflect-metadata)\n- [core-js (core-js/es7/reflect)](https://www.npmjs.com/package/core-js)\n- [reflection](https://www.npmjs.com/package/@abraham/reflection)\n\nReflect polyfill 的导入应该只添加一次，并且在使用 DI 之前：\n\n```typescript\n// main.ts\nimport 'reflect-metadata';\n\n// 你的代码...\n```\n\n## Quick Start\n\n让我们从一个简单的例子开始:\n\n```ts\nimport { Injector } from '@opensumi/di';\n\n// 创建一个 Injector，这是一个 IoC 容器\nconst injector = new Injector();\n\nconst TokenA = Symbol('TokenA');\ninjector.addProviders({\n  token: TokenA,\n  useValue: 1,\n});\ninjector.get(TokenA) === 1; // true\n```\n\nThe `Injector` class is the starting point of all things. We create a `injector`, and we add a provider into it:\n\n```ts\ninjector.addProviders({\n  token: TokenA,\n  useValue: 1,\n});\n```\n\nWe use a `ValueProvider` here, and its role is to provide a value:\n\n```ts\ninterface ValueProvider {\n  token: Token;\n  useValue: any;\n}\n```\n\nWe have the following several kinds of the provider. According to the different Provider kinds, Injector will use different logic to provide the value that you need.\n\n```ts\ntype Provider = ClassProvider | ValueProvider | FactoryProvider | AliasProvider;\n```\n\nA token is used to find the real value in the Injector, so token should be a global unique value.\n\n```ts\ntype Token = string | symbol | Function;\n```\n\nand now we want get value from the `Injector`, just use `Injector.get`:\n\n```ts\ninjector.get(TokenA) === 1;\n```\n\n### Providers\n\n这是目前支持的 Provider 类型:\n\n#### ClassProvider\n\n定义一个 Token 使用某个特定的构造函数的时候会用到的 Provider。\n\n```ts\ninterface ClassProvider {\n  token: Token;\n  useClass: ConstructorOf\u003cany\u003e;\n}\n```\n\n在依赖反转之后，构造函数都依赖抽象而不依赖实例的时候会非常有效。比如下面的例子：\n\n```ts\ninterface Drivable {\n  drive(): void;\n}\n\n@Injectable()\nclass Student {\n  @Autowired('Drivable')\n  mBike: Drivable;\n\n  goToSchool() {\n    console.log('go to school');\n    this.mBike.drive();\n  }\n}\n```\n\n学生对象依赖的是一个可驾驶的交通工具，可以在创建对象的时候提供一个自行车，也可以在创建的时候提供一个汽车：\n\n```ts\n@Injectable()\nclass Car implements Drivable {\n  drive() {\n    console.log('by car');\n  }\n}\n\ninjector.addProviders(Student, {\n  token: 'Drivable',\n  useClass: Car,\n});\n\nconst student = injector.get(Student);\nstudent.goToSchool(); // print 'go to school by car'\n```\n\n#### ValueProvider\n\nThis provider is used to provide a value:\n\n```ts\ninterface ValueProvider {\n  token: Token;\n  useValue: any;\n}\n```\n\n```ts\nconst TokenA = Symbol('TokenA');\ninjector.addProviders({\n  token: TokenA,\n  useValue: 1,\n});\ninjector.get(TokenA) === 1; // true\n```\n\n#### FactoryProvider\n\n提供一个函数进行对象实例创建的 Provider。\n\n```ts\ninterface FactoryFunction\u003cT = any\u003e {\n  (injector: Injector): T;\n}\ninterface FactoryProvider {\n  token: Token;\n  useFactory: FactoryFunction\u003cT\u003e;\n}\n```\n\n同时也提供了一些工厂模式的帮助函数：\n\n1. `asSingleton`\n\nYou can implement a singleton factory by using this helper:\n\n```ts\nconst provider = {\n  token,\n  useFactory: asSingleton(() =\u003e new A()),\n};\n```\n\n#### AliasProvider\n\nSets a token to the alias of an existing token.\n\n```ts\ninterface AliasProvider {\n  // New Token\n  token: Token;\n  // Existing Token\n  useAlias: Token;\n}\n```\n\nand then you can use:\n\n```ts\nconst TokenA = Symbol('TokenA');\nconst TokenB = Symbol('TokenB');\ninjector.addProviders(\n  {\n    token: TokenA,\n    useValue: 1,\n  },\n  {\n    token: TokenB,\n    useAlias: TokenA,\n  },\n);\ninjector.get(TokenA) === 1; // true\ninjector.get(TokenB) === 1; // true\n```\n\n### 对构造函数进行注入\n\n在下面这个例子里，你会发现 `class B` 依赖于 `class A`，并且在构造函数的参数列表中声明了这个依赖关系，所以在 `B` 的实例创建过程中，Injector 会自动创建 `A` 的实例，并且注入到 `B` 的实例中。\n\n```ts\n@Injectable()\nclass A {\n  constructor() {\n    console.log('Create A');\n  }\n}\n\n@Injectable()\nclass B {\n  constructor(public a: A) {}\n}\n\nconst injector = new Injector();\ninjector.addProviders(A, B);\n\nconst b = injector.get(B); // 打印 'Create A'\nconsole.log(b.a instanceof A); // 打印 'true'\n```\n\n### 使用 `@Autowired()` 进行动态注入\n\n```ts\n@Injectable()\nclass A {\n  constructor() {\n    console.log('Create A');\n  }\n}\n\n@Injectable()\nclass B {\n  @Autowired()\n  a: A;\n}\n\nconst injector = new Injector();\ninjector.addProviders(A, B);\n\nconst b = injector.get(B);\nconsole.log(b.a instanceof A); // 1. 打印 'Create A', 2. 打印 'true'\n```\n\n### 可以创建单例或者多例\n\n```ts\n@Injectable()\nclass Singleton {\n  constructor() {}\n}\n\n@Injectable({ multiple: true })\nclass Multiton {\n  constructor() {}\n}\n\nconst injector = new Injector();\ninjector.addProviders(Singleton, Multiton);\n\nconst single1 = injector.get(Singleton);\nconst single2 = injector.get(Singleton);\nconsole.log(single1 === single2); // print 'true'\n\nconst multiple1 = injector.get(Multiton);\nconst multiple2 = injector.get(Multiton);\nconsole.log(multiple1 === multiple2); // print 'false'\n```\n\n### 类型依赖抽象而不是依赖实现的用法\n\n```ts\nconst LOGGER_TOKEN = Symbol('LOGGER_TOKEN');\n\ninterface Logger {\n  log(msg: string): void;\n}\n\n@Injectable()\nclass App {\n  @Autowired(LOGGER_TOKEN)\n  logger: Logger;\n}\n\n@Injectable()\nclass LoggerImpl implements Logger {\n  log(msg: string) {\n    console.log(msg);\n  }\n}\n\nconst injector = new Injector();\ninjector.addProviders(App);\ninjector.addProviders({\n  token: LOGGER_TOKEN,\n  useClass: LoggerImpl,\n});\n\nconst app = injector.get(App);\nconsole.log(app.logger instanceof LoggerImpl); // 打印 'true'\n```\n\n### 使用抽象函数作为 Token 进行依赖注入\n\n```ts\nabstract class Logger {\n  abstract log(msg: string): void;\n}\n\n@Injectable()\nclass LoggerImpl implements Logger {\n  log(msg: string) {\n    console.log(msg);\n  }\n}\n\n@Injectable()\nclass App {\n  @Autowired()\n  logger: Logger;\n}\n\nconst injector = new Injector();\ninjector.addProviders(App);\ninjector.addProviders({\n  token: Logger,\n  useClass: LoggerImpl,\n});\n\nconst app = injector.get(App);\nconsole.log(app.logger instanceof LoggerImpl); // print 'true'\n```\n\n## API\n\n### decorator: @Injectable\n\n```ts\ninterface InstanceOpts {\n  multiple?: boolean;\n}\nfunction Injectable(opts?: InstanceOpts): ClassDecorator;\n\n@Injectable({ multiple: true })\nclass A {}\n\nconst injector = new Injector([A]);\n\nconst a = injector.get(A);\nconsole.log(injector.hasInstance(a)); // print 'false'\n```\n\n所有需要被 Injector 创建的构造函数都应该使用这个装饰器修饰才可以正常使用，否则会报错。\n\n- multiple: 是否启用多例模式，一旦启用了多例模式之后，Injector 将不会持有实例对象的引用。\n\n### decorator: @Autowired\n\n```ts\nfunction Autowired(token?: Token): PropertyDecorator;\n\n@Injectable()\nclass A {}\n\n@Injectable()\nclass B {\n  @Autowired()\n  a: A;\n}\n```\n\n修饰一个属性会被注册器动态创建依赖实例，而这个依赖实例只有在被使用的时候才会被创建出来。比如上面的例子中，只有访问到 `b.a` 的时候，才会创建 A 的实例。\n\n\u003e 需要注意的是，因为 Autowired 依赖着 Injector 的实例，所以只有从 Injector 创建出来的对象可以使用这个装饰器\n\n### decorator: @Inject\n\n```ts\nfunction Inject(token: string | symbol): ParameterDecorator;\n\ninterface IA {\n  log(): void;\n}\n\n@Injectable()\nclass B {\n  constructor(@Inject('IA') a: IA) {}\n}\n```\n\n在构造函数进行依赖注入的时候，需要特别指定依赖 Token 的时候的装饰器。当一个构造函数依赖某个抽象，并且这个抽象是在构造函数中传递进来的时候，会需要使用这个装饰器。\n\n### Injector.get\n\n```ts\ninterface Injector\u003cT extends Token\u003e {\n  get(token: ConstructorOf\u003cany\u003e, args?: ConstructorParameters\u003cT\u003e, opts?: InstanceOpts): TokenResult\u003cT\u003e;\n  get(token: T, opts?: InstanceOpts): TokenResult\u003cT\u003e;\n}\n```\n\n从 Injector 获取一个对象实例的方法，如果传递的是一个构造函数，第二个参数可以传递构造函数 Arguments 数据，此时将会直接将构造函数创建实例返回，并附加依赖注入的功能，此时的构造函数不需要被 Injectable 装饰也能正常创建对象。例如下面这样：\n\n```ts\n@Injectable()\nclass A {}\n\nclass B {\n  @Autowired()\n  a: A;\n}\n\nconst injector = new Injector([A]);\nconst b = injector.get(B, []);\nconsole.log(b.a instanceof A); // print 'true'\n```\n\n### Injector.hasInstance\n\nWhether have an instantiated object in the Injector.\n\n### Injector.disposeOne / Injector.disposeAll\n\n可以使用 `Injector.disposeOne` 和 `Injector.disposeAll` 来释放 Token。\n\n这两个方法会从 DI 容器中删除当前已创建的实例，并尝试调用这个实例的 `dispose` 方法（可以没有）。\n\n### markInjectable\n\n```ts\nimport { markInjectable } from '@opensumi/di';\nimport { Editor } from 'path/to/package';\n\nmarkInjectable(Editor);\n```\n\nYou can use this function to mark some Class as Injectable.\n\n## Examples\n\nSee More Examples [in the test case](test/use-case.test.ts).\n\n## FAQ\n\nPlease see [FAQ.md](docs/faq.md).\n\n## Related Efforts\n\n- [Angular](https://angular.io/guide/dependency-injection) Dependency injection in Angular\n- [injection-js](https://github.com/mgechev/injection-js) It is an extraction of the Angular's ReflectiveInjector.\n- [InversifyJS](https://github.com/inversify/InversifyJS) A powerful and lightweight inversion of control container for JavaScript \u0026 Node.js apps powered by TypeScript.\n- [power-di](https://github.com/zhang740/power-di) A lightweight Dependency Injection library.\n","funding_links":[],"categories":["Repository"],"sub_categories":["Inversion of control / Dependency Injection (Ioc/DI)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensumi%2Fdi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopensumi%2Fdi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensumi%2Fdi/lists"}