0
0
微信打赏支付宝打赏
扫一扫,请我喝杯咖啡
0
前端VSCode风格依赖注入(DI)使用指南

VSCode源码地址

VSCode依赖注入源码目录结构

1234567891011121314151617
📦src ┣ 📂vs ┃ ┣ 📂platform ┃ ┃ ┣ 📂instantiation ┃ ┃ ┃ ┣ 📂common ┃ ┃ ┃ ┃ ┣ 📜descriptors.ts // 依赖描述符 ┃ ┃ ┃ ┃ ┣ 📜extensions.ts // 插件系统和装饰器 ┃ ┃ ┃ ┃ ┣ 📜graph.ts // 依赖关系图 ┃ ┃ ┃ ┃ ┣ 📜instantiation.ts // 依赖注入类型和装饰器 ┃ ┃ ┃ ┃ ┣ 📜instantiationService.ts // 实例化服务 ┃ ┃ ┃ ┃ ┗ 📜serviceCollection.ts // 服务收集 ┃ ┃ ┃ ┗ 📂test ┃ ┃ ┃ ┃ ┗ 📂common ┃ ┃ ┃ ┃ ┃ ┣ 📜graph.test.ts // 依赖关系图单元测试 ┃ ┃ ┃ ┃ ┃ ┣ 📜instantiationService.test.ts // 实例化服务单元测试 ┃ ┃ ┃ ┃ ┃ ┗ 📜instantiationServiceMock.ts // 实例化服务模拟

本篇不剖析底层代码,只讲怎么用,想研究架构的可以参考下面文章
底层原理

📚 目录

  1. 核心概念
  2. 快速开始
  3. 核心API详解
  4. 实战示例
  5. 高级用法
  6. 常见问题

核心概念

什么是依赖注入?

依赖注入是一种设计模式,它将对象的创建和依赖关系的管理交给外部容器处理,而不是在对象内部硬编码依赖。

这套DI系统的关键要素

  • ServiceIdentifier:服务的唯一标识符(通常由createDecorator创建)
  • SyncDescriptor:服务的描述符,包含构造函数和参数
  • ServiceCollection:服务注册表,存储所有服务
  • InstantiationService:实例化引擎,负责创建和管理服务实例

快速开始

1. 导入核心模块

123456789
import { InstantiationService, ServiceCollection, SyncDescriptor, registerSingleton, InstantiationType, createDecorator } from './core/instantiation';

2. 定义服务接口和标识符

123456789
// 定义服务接口 export interface ILoggerService { log(message: string): void; error(message: string): void; } // 创建服务标识符(这是关键!) export const ILoggerService = createDecorator<ILoggerService>('loggerService');

3. 实现服务

1234567891011
// 实现服务类 export class LoggerService implements ILoggerService { log(message: string): void { console.log(`[LOG] ${message}`); } error(message: string): void { console.error(`[ERROR] ${message}`); } }

4. 注册服务

12345678910111213
// 方式一:使用 registerSingleton registerSingleton( ILoggerService, LoggerService, InstantiationType.Delayed // 延迟实例化(推荐) ); // 方式二:使用 ServiceCollection + InstantiationService const serviceCollection = new ServiceCollection(); serviceCollection.set(ILoggerService, new SyncDescriptor(LoggerService)); const instantiationService = new InstantiationService(serviceCollection);

5. 使用服务

123456789101112131415
// 创建需要依赖注入的类 export class App { constructor( @ILoggerService private readonly logger: ILoggerService ) {} run() { this.logger.log('应用启动成功!'); } } // 通过 InstantiationService 创建实例 const app = instantiationService.createInstance(App); app.run(); // 输出:[LOG] 应用启动成功!

核心API详解

1. createDecorator<T>(serviceId: string): ServiceIdentifier<T>

创建服务标识符,用于标识和注入服务。

1234
// 定义服务标识符 export const IUserService = createDecorator<IUserService>('userService'); export const IConfigService = createDecorator<IConfigService>('configService');

2. registerSingleton(id, ctor, instantiationType)

注册单例服务到全局注册表。

12345678
import { registerSingleton, InstantiationType } from './core/instantiation'; // 立即实例化(启动时创建) registerSingleton(ILoggerService, LoggerService, InstantiationType.Eager); // 延迟实例化(首次使用时创建,推荐) registerSingleton(IUserService, UserService, InstantiationType.Delayed);

3. SyncDescriptor<T>

服务的描述符,用于描述如何创建服务实例。

123456789101112
import { SyncDescriptor } from './core/instantiation'; // 基本用法 const descriptor = new SyncDescriptor(LoggerService); // 带静态参数 const descriptorWithArgs = new SyncDescriptor( UserService, ['staticArg1', 'staticArg2'], // 静态参数 true // 支持延迟实例化 );

4. ServiceCollection

服务注册表容器,存储服务的实例或描述符。

123456789101112131415161718
import { ServiceCollection } from './core/instantiation'; const collection = new ServiceCollection(); // 注册服务实例(已实例化) collection.set(ILoggerService, new LoggerService()); // 注册服务描述符(延迟实例化) collection.set(IUserService, new SyncDescriptor(UserService)); // 检查服务是否已注册 if (collection.has(ILoggerService)) { const service = collection.get(ILoggerService); } // 覆盖已注册的服务 const oldService = collection.set(ILoggerService, new MockLoggerService());

5. InstantiationService

核心实例化服务,负责创建和管理所有服务实例。

1234567891011121314151617181920
import { InstantiationService, ServiceCollection } from './core/instantiation'; // 创建根实例化服务 const rootServices = new ServiceCollection(); const instantiationService = new InstantiationService(rootServices); // 创建子实例化服务(可覆盖部分服务) const childServices = new ServiceCollection(); childServices.set(IConfigService, new SyncDescriptor(MockConfigService)); const childInstantiationService = instantiationService.createChild(childServices); // 创建实例 const myService = instantiationService.createInstance(MyService); // 调用函数并注入服务 instantiationService.invokeFunction((accessor) => { const logger = accessor.get(ILoggerService); logger.log('通过 invokeFunction 调用'); });

实战示例

示例1:用户管理系统

示例2:事件驱动架构


高级用法

1. 循环依赖检测

DI系统会自动检测并报告循环依赖:

1234567891011
// ❌ 错误的用法(会导致循环依赖) export class ServiceA { constructor(@IServiceB private serviceB: IServiceB) {} } export class ServiceB { constructor(@IServiceA private serviceA: IServiceA) {} } // 实例化时会抛出 CyclicDependencyError

2. 服务覆盖(用于测试)

12345678910111213141516171819
// 生产环境服务 class RealApiService implements IApiService { async fetch(url: string) { return fetch(url); } } // Mock服务用于测试 class MockApiService implements IApiService { async fetch(url: string) { return { data: 'mock data' }; } } // 在测试环境中覆盖服务 const testServices = new ServiceCollection(); testServices.set(IApiService, new SyncDescriptor(MockApiService)); const testInstantiationService = instantiationService.createChild(testServices);

3. 延迟加载优化

12345678910111213
// 使用 InstantiationType.Delayed 优化启动性能 registerSingleton(IHeavyService, HeavyService, InstantiationType.Delayed); // 首次使用时才实例化 class App { constructor(@IHeavyService private heavyService: IHeavyService) {} async onUserAction() { // 此时才会真正创建 HeavyService 实例 await this.heavyService.performHeavyTask(); } }

4. 可选依赖

123456789101112131415
// 使用 @optional 装饰器处理可选依赖 export class FeatureService { constructor( @IRequiredService private required: IRequiredService, @optional(IExternalService) private external?: IExternalService ) {} doWork() { this.required.doMandatoryThing(); if (this.external) { this.external.doOptionalThing(); } } }

常见问题

Q1: 为什么需要 createDecorator 创建标识符?

A: createDecorator 创建的服务标识符既是TypeScript装饰器,又是运行时的唯一标识。它让DI系统能够:

  • 在编译时提供类型安全
  • 在运行时识别要注入的服务
  • 支持参数位置追踪

Q2: Eager 和 Delayed 模式如何选择?

A:

  • Delayed(推荐):服务在首次使用时实例化,提升启动性能
  • Eager:服务在容器创建时立即实例化,适用于必需的、轻量级的服务

Q3: 如何在React/Vue组件中使用?

A: 将 instantiationService 注入到组件上下文:

Q4: 如何调试依赖注入问题?

A: 启用跟踪功能(如果支持):

1234567
// 在创建 InstantiationService 时启用跟踪 const instantiationService = new InstantiationService( serviceCollection, true // enableTracing ); // 会输出详细的实例化日志,帮助定位问题

Q5: 可以动态注册服务吗?

A: 可以,但在实例化服务之前注册:

12345678
// ✅ 正确:在创建实例前注册 serviceCollection.set(IMyService, new SyncDescriptor(MyService)); const instance = instantiationService.createInstance(MyComponent); // ❌ 错误:实例化后注册可能无效 const instance = instantiationService.createInstance(MyComponent); serviceCollection.set(IMyService, new SyncDescriptor(MyService)); // 已创建的实例不会重新注入

总结

这套VSCode风格的DI系统提供了:

类型安全:完全的TypeScript支持
性能优化:支持延迟实例化
循环依赖检测:自动检测并报错
层次化容器:支持服务覆盖和隔离
测试友好:轻松Mock服务

通过依赖注入,你的代码将更加:

  • 可维护:依赖关系清晰可见
  • 可测试:轻松替换为Mock对象
  • 可扩展:添加新服务不影响现有代码

这份文档涵盖了从入门到实战的全部内容。建议你先从快速开始部分着手,在实际项目中逐步尝试,遇到具体问题再查阅对应章节。祝你使用愉快!🎉

游客

全部评论 (0)

暂无评论,快来抢吧~