1. 什么是依赖注入
依赖注入(Dependency Injection,DI) 是一种设计模式:组件或服务不自己创建依赖对象,而是由外部(注入器)提供。Angular 的 DI 系统是框架的核心支柱,贯穿整个应用架构。
没有 DI(紧耦合)
class UserComponent {
private service =
new UserService(); // 自己 new
// 问题:无法替换实现,难以测试
}
有 DI(松耦合)
class UserComponent {
constructor(
private service: UserService
) {} // Angular 自动提供
}
2. 注入器树(Injector Tree)
Angular 的注入器形成一棵树,与组件树平行。服务注册在哪一层,决定了它的作用域和实例数量:
-
Root Injector
应用级别的注入器,
providedIn: 'root'的服务注册在此处,整个应用共享同一个实例(单例)。支持 Tree Shaking(未被使用的服务会从构建产物中移除)。 - Platform Injector 平台级别,多个 Angular 应用共享同一页面时使用。
-
Element Injector
每个组件都有自己的注入器,在
@Component.providers中注册的服务,每个组件实例拥有独立的服务实例。
3. @Injectable 装饰器
创建一个可注入服务,需要用 @Injectable() 装饰,并在 providedIn 中声明注册位置:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
@Injectable({
providedIn: 'root' // 注册到根注入器(应用单例)
})
export class UserService {
private baseUrl = '/api/users';
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.baseUrl);
}
getUser(id: number): Observable<User> {
return this.http.get<User>(`${this.baseUrl}/${id}`);
}
createUser(data: Partial<User>): Observable<User> {
return this.http.post<User>(this.baseUrl, data);
}
updateUser(id: number, data: Partial<User>): Observable<User> {
return this.http.put<User>(`${this.baseUrl}/${id}`, data);
}
deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`${this.baseUrl}/${id}`);
}
}
4. providedIn 策略详解
| 值 | 说明 | 适用场景 |
|---|---|---|
'root' | 注册到应用根注入器,全局单例 | 大多数服务(HTTP、认证、配置) |
'platform' | 注册到平台级别,跨 Angular 应用共享 | 多个 Angular 应用共存时 |
'any' | 每个懒加载模块/组件各自拥有一个实例 | 需要模块级别隔离的服务 |
| 组件 providers | 每个组件实例有独立的服务实例 | 有状态的组件级服务 |
5. inject() 函数(Angular 14+)
inject() 是 Angular 14 引入的函数式 API,可以在注入上下文中(构造函数、字段初始化器、工厂函数)直接注入依赖,无需通过构造函数参数。在 Standalone 和 Signals 时代,这是更现代的写法。
import { Component, inject, OnInit } from '@angular/core';
import { UserService } from './user.service';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';
@Component({
selector: 'app-user-list',
standalone: true,
template: `...`
})
export class UserListComponent implements OnInit {
// 用 inject() 直接在字段初始化时注入(更简洁)
private userService = inject(UserService);
private router = inject(Router);
private auth = inject(AuthService);
users: User[] = [];
ngOnInit() {
this.userService.getUsers().subscribe(users => {
this.users = users;
});
}
}
inject() 的优势
- 更适合函数式编程风格
- 可以在自定义 Hook(工厂函数)中使用,实现逻辑复用
- 与 Signals 配合更自然
- 构造函数不再需要长列表参数
// 可复用的"Hook"函数(类比 React 自定义 Hook)
function useCurrentUser() {
const auth = inject(AuthService);
const router = inject(Router);
return {
user: auth.currentUser,
logout: () => {
auth.logout();
router.navigate(['/login']);
}
};
}
@Component({ /* ... */ })
export class HeaderComponent {
protected currentUser = useCurrentUser();
// 直接在模板中用 currentUser.user 和 currentUser.logout()
}
6. 组件级别的服务(分层注入)
有时你需要每个组件实例拥有独立的服务实例(如购物车状态)。在 @Component.providers 中注册服务即可实现:
import { Injectable, signal } from '@angular/core';
@Injectable() // 注意:不加 providedIn
export class CartService {
private items = signal<CartItem[]>([]);
count = computed(() => this.items().length);
total = computed(() =>
this.items().reduce((sum, i) => sum + i.price * i.qty, 0)
);
addItem(item: CartItem) {
this.items.update(items => [...items, item]);
}
}
// 在组件中提供,每个组件实例获得独立的 CartService
@Component({
selector: 'app-checkout',
standalone: true,
providers: [CartService], // 组件级提供
template: `...`
})
export class CheckoutComponent {
protected cart = inject(CartService);
}
7. 完整实战:UserService + 认证服务
// auth.service.ts — 认证服务(根单例)
@Injectable({ providedIn: 'root' })
export class AuthService {
private currentUser = signal<User | null>(null);
private http = inject(HttpClient);
private router = inject(Router);
isLoggedIn = computed(() => this.currentUser() !== null);
user = this.currentUser.asReadonly();
login(email: string, password: string): Observable<User> {
return this.http.post<User>('/api/auth/login', { email, password }).pipe(
tap(user => {
this.currentUser.set(user);
localStorage.setItem('token', user.token);
})
);
}
logout(): void {
this.currentUser.set(null);
localStorage.removeItem('token');
this.router.navigate(['/login']);
}
}
本章小结:依赖注入是 Angular 解耦架构的核心。providedIn: 'root' 创建全局单例,组件级 providers 创建局部实例,inject() 函数是现代 Angular 推荐的注入方式。下一章学习路由系统。