🎬 HoRain 云小助手个人主页

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

目录

⛳️ 推荐

1. 基础概念和设置

创建可注入的服务

在组件中注入服务

2. 提供者的不同配置方式

1. 在根级别提供(推荐)

2. 在模块级别提供

3. 在组件级别提供

3. 不同类型的提供者

类提供者(最常用)

值提供者

工厂提供者

别名提供者

4. 分层注入器实战

模块级服务

组件树中的注入层次

5. 实际项目中的高级用法

配置服务

HTTP 拦截器(依赖注入的典型应用)

可选的依赖注入

6. 测试中的依赖注入

单元测试中的模拟依赖

7. 最佳实践和模式

1. 使用 InjectionToken 提供配置

2. 分层服务架构

3. 环境特定的提供者

8. 常见问题解决

循环依赖问题

动态组件注入

依赖注入总结表


Angular 的依赖注入系统是其核心特性之一,非常强大和灵活。下面详细讲解如何在 Angular 中实现和使用依赖注入。

1. 基础概念和设置

创建可注入的服务

// user.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root' // 在根注入器提供,整个应用单例
})
export class UserService {
  private users: any[] = [];

  getUsers() {
    return this.users;
  }

  addUser(user: any) {
    this.users.push(user);
  }
}

在组件中注入服务

// user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngFor="let user of users">
      {{ user.name }}
    </div>
  `
})
export class UserListComponent implements OnInit {
  users: any[] = [];

  // 通过构造函数注入
  constructor(private userService: UserService) {}

  ngOnInit() {
    this.users = this.userService.getUsers();
  }
}

2. 提供者的不同配置方式

1. 在根级别提供(推荐)

@Injectable({
  providedIn: 'root'  // 应用级单例
})
export class ApiService {
  // 服务实现
}

2. 在模块级别提供

// app.module.ts
@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [
    UserService,  // 模块级单例
    { provide: 'API_URL', useValue: 'https://api.example.com' }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

3. 在组件级别提供

@Component({
  selector: 'app-user',
  template: `...`,
  providers: [UserService]  // 组件级实例(每个组件都有自己的实例)
})
export class UserComponent {
  constructor(private userService: UserService) {}
}

3. 不同类型的提供者

类提供者(最常用)

providers: [UserService]
// 等价于
providers: [{ provide: UserService, useClass: UserService }]

值提供者

// 提供常量或配置
providers: [
  { provide: 'API_CONFIG', useValue: { 
    baseUrl: 'https://api.example.com',
    timeout: 5000 
  }},
  { provide: 'APP_VERSION', useValue: '1.0.0' }
]

// 注入使用
constructor(
  @Inject('API_CONFIG') private config: any,
  @Inject('APP_VERSION') private version: string
) {}

工厂提供者

// 根据条件创建不同的实例
export function loggerFactory(isProduction: boolean): LoggerService {
  return isProduction ? 
    new ProductionLogger() : 
    new DevelopmentLogger();
}

providers: [
  { provide: 'IS_PRODUCTION', useValue: false },
  { 
    provide: LoggerService, 
    useFactory: loggerFactory,
    deps: ['IS_PRODUCTION']  // 依赖注入
  }
]

别名提供者

// 为一个服务提供多个令牌
class AdvancedUserService extends UserService {
  // 扩展功能
}

providers: [
  AdvancedUserService,
  { provide: UserService, useExisting: AdvancedUserService }  // 别名
]

4. 分层注入器实战

模块级服务

// shared.module.ts
@NgModule({
  providers: [SharedService]  // 在共享模块中提供
})
export class SharedModule { }

// feature.module.ts
@NgModule({
  imports: [SharedModule],
  providers: [FeatureService]  // 在特性模块中提供
})
export class FeatureModule { }

组件树中的注入层次

// 父组件提供服务
@Component({
  selector: 'app-parent',
  template: `<app-child></app-child>`,
  providers: [DataService]  // 提供给所有子组件
})
export class ParentComponent {}

// 子组件可以注入
@Component({
  selector: 'app-child',
  template: `...`
})
export class ChildComponent {
  constructor(private dataService: DataService) {}  // 注入父组件提供的服务
}

5. 实际项目中的高级用法

配置服务

// config.service.ts
import { Injectable, InjectionToken } from '@angular/core';

export interface AppConfig {
  apiUrl: string;
  timeout: number;
  retryCount: number;
}

export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  constructor(@Inject(APP_CONFIG) private config: AppConfig) {}

  get apiUrl(): string {
    return this.config.apiUrl;
  }

  get timeout(): number {
    return this.config.timeout;
  }
}

// 在模块中配置
@NgModule({
  providers: [
    {
      provide: APP_CONFIG,
      useValue: {
        apiUrl: 'https://api.example.com',
        timeout: 5000,
        retryCount: 3
      }
    }
  ]
})
export class AppModule { }

HTTP 拦截器(依赖注入的典型应用)

// auth.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const authToken = this.authService.getToken();
    
    const authReq = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${authToken}`)
    });
    
    return next.handle(authReq);
  }
}

// 在模块中注册拦截器
@NgModule({
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true  // 允许多个拦截器
    }
  ]
})
export class AppModule { }

可选的依赖注入

import { Optional, SkipSelf } from '@angular/core';

@Component({...})
export class MyComponent {
  // 如果服务不存在,不会报错,service 为 null
  constructor(@Optional() private optionalService: OptionalService) {}

  // 跳过自身查找器,从父注入器查找
  constructor(@SkipSelf() private parentService: ParentService) {}
}

6. 测试中的依赖注入

单元测试中的模拟依赖

// user.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
import { ApiService } from './api.service';

describe('UserService', () => {
  let service: UserService;
  let mockApiService: jasmine.SpyObj<ApiService>;

  beforeEach(() => {
    // 创建模拟服务
    mockApiService = jasmine.createSpyObj('ApiService', ['getUsers']);

    TestBed.configureTestingModule({
      providers: [
        UserService,
        { provide: ApiService, useValue: mockApiService }  // 注入模拟对象
      ]
    });

    service = TestBed.inject(UserService);
  });

  it('应该从API获取用户', () => {
    mockApiService.getUsers.and.returnValue([{id: 1, name: 'John'}]);
    
    const users = service.getUsers();
    expect(users.length).toBe(1);
    expect(mockApiService.getUsers).toHaveBeenCalled();
  });
});

7. 最佳实践和模式

1. 使用 InjectionToken 提供配置

// tokens.ts
import { InjectionToken } from '@angular/core';

export const API_BASE_URL = new InjectionToken<string>('API_BASE_URL');
export const LOG_LEVEL = new InjectionToken<'debug' | 'info' | 'error'>('LOG_LEVEL');

// 使用
providers: [
  { provide: API_BASE_URL, useValue: 'https://api.example.com' },
  { provide: LOG_LEVEL, useValue: 'debug' }
]

2. 分层服务架构

// 数据层服务
@Injectable({ providedIn: 'root' })
export class UserApiService {
  constructor(private http: HttpClient) {}
  
  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }
}

// 业务逻辑层服务
@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private userApi: UserApiService) {}
  
  getActiveUsers(): Observable<User[]> {
    return this.userApi.getUsers().pipe(
      map(users => users.filter(user => user.active))
    );
  }
}

3. 环境特定的提供者

// environment.ts
export const environment = {
  production: false,
  apiUrl: 'https://dev.api.example.com'
};

// 根据环境提供不同的服务
const loggerServiceFactory = (environment: any) => {
  return environment.production ? 
    new ProductionLogger() : 
    new DevelopmentLogger();
};

providers: [
  { provide: 'ENVIRONMENT', useValue: environment },
  {
    provide: LoggerService,
    useFactory: loggerServiceFactory,
    deps: ['ENVIRONMENT']
  }
]

8. 常见问题解决

循环依赖问题

// 使用 @Inject 和 forwardRef 解决循环依赖
@Component({...})
export class ComponentA {
  constructor(
    @Inject(forwardRef(() => ComponentB)) private componentB: ComponentB
  ) {}
}

@Component({...})
export class ComponentB {
  constructor(private componentA: ComponentA) {}
}

动态组件注入

// 在运行时动态创建组件并注入服务
export class DynamicComponentService {
  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private injector: Injector
  ) {}

  createDynamicComponent(componentType: Type<any>, parentInjector?: Injector) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);
    
    // 创建自定义注入器
    const injector = Injector.create({
      parent: parentInjector || this.injector,
      providers: [
        { provide: DynamicDataService, useClass: DynamicDataService }
      ]
    });
    
    return componentFactory.create(injector);
  }
}

依赖注入总结表

场景

推荐方式

说明

全局单例服务

providedIn: 'root'

应用级单例,推荐大多数服务使用

模块特定服务

NgModuleproviders中提供

模块级单例

组件树服务

在组件 providers中提供

组件及其子组件共享实例

配置值

使用 InjectionToken+ useValue

提供配置常量

条件创建

使用工厂提供者 useFactory

根据运行时条件创建实例

测试模拟

在测试模块中覆盖提供者

使用 useValueuseClass模拟

​核心要点:​

  1. ​理解注入器层次结构​​ - 根注入器 → 模块注入器 → 组件注入器

  2. ​选择合适的提供范围​​ - 根据服务的使用范围决定提供级别

  3. ​善用各种提供者类型​​ - 类、值、工厂、别名等

  4. ​测试友好​​ - 依赖注入使得测试时模拟依赖非常容易

  5. ​避免循环依赖​​ - 使用 @InjectforwardRef解决

Angular 的依赖注入系统是其框架的基石,正确使用可以创建出松耦合、可测试、可维护的应用程序。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐