06 - 服务与依赖注入
📖 学习目标
通过本章节学习,您将掌握:
- 服务的概念和作用
- 依赖注入系统
- 创建和使用服务
- 服务的作用域
- 服务间通信
- 高级服务模式
🎯 核心概念
1. 什么是服务?
服务是Angular中用于封装业务逻辑、数据访问和共享功能的类。服务的主要特点:
- 单一职责:每个服务专注于特定功能
- 可重用性:可在多个组件中使用
- 可测试性:易于单元测试
- 依赖注入:通过DI系统管理依赖
2. 依赖注入系统
Angular的依赖注入系统负责:
- 创建服务实例
- 管理服务生命周期
- 解析依赖关系
- 提供服务实例
3. 服务作用域
应用级别 (Application Level)
├── 根注入器 (Root Injector)
│ └── 单例服务
├── 模块级别 (Module Level)
│ └── 模块内共享服务
└── 组件级别 (Component Level)
└── 组件实例服务
🏗️ 创建服务
1. 使用CLI创建服务
bash
# 创建服务
ng generate service user
# 或简写
ng g s user
# 创建服务并指定路径
ng g s services/user
# 创建服务并注册为提供者
ng g s user --skip-tests
2. 基本服务结构
typescript
// user.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // 根级别提供
})
export class UserService {
private users: User[] = [];
constructor() { }
// 获取所有用户
getUsers(): User[] {
return this.users;
}
// 添加用户
addUser(user: User): void {
this.users.push(user);
}
// 根据ID获取用户
getUserById(id: number): User | undefined {
return this.users.find(user => user.id === id);
}
// 更新用户
updateUser(id: number, updatedUser: Partial<User>): boolean {
const index = this.users.findIndex(user => user.id === id);
if (index !== -1) {
this.users[index] = { ...this.users[index], ...updatedUser };
return true;
}
return false;
}
// 删除用户
deleteUser(id: number): boolean {
const index = this.users.findIndex(user => user.id === id);
if (index !== -1) {
this.users.splice(index, 1);
return true;
}
return false;
}
}
export interface User {
id: number;
name: string;
email: string;
role: string;
createdAt: Date;
}
🔧 依赖注入详解
1. 构造函数注入
typescript
// 组件中注入服务
import { Component } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent {
users: User[] = [];
constructor(private userService: UserService) {
this.users = this.userService.getUsers();
}
}
2. 使用Inject装饰器
typescript
import { Component, Inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent {
users: User[] = [];
constructor(@Inject(UserService) private userService: UserService) {
this.users = this.userService.getUsers();
}
}
3. 可选依赖注入
typescript
import { Component, Optional } from '@angular/core';
import { LoggerService } from './logger.service';
@Component({
selector: 'app-component',
template: '<div>组件内容</div>'
})
export class MyComponent {
constructor(@Optional() private logger?: LoggerService) {
if (this.logger) {
this.logger.log('组件初始化');
}
}
}
🎯 服务作用域管理
1. 根级别服务(单例)
typescript
@Injectable({
providedIn: 'root'
})
export class GlobalService {
// 整个应用共享一个实例
}
2. 模块级别服务
typescript
// 在模块中提供
@NgModule({
providers: [UserService],
// ...
})
export class UserModule { }
// 服务定义
@Injectable()
export class UserService {
// 模块内共享实例
}
3. 组件级别服务
typescript
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
providers: [UserService] // 每个组件实例都有自己的服务实例
})
export class UserComponent {
constructor(private userService: UserService) {}
}
🔄 服务间通信
1. 使用Subject进行通信
typescript
// 通信服务
@Injectable({
providedIn: 'root'
})
export class CommunicationService {
private messageSubject = new Subject<string>();
public message$ = this.messageSubject.asObservable();
sendMessage(message: string) {
this.messageSubject.next(message);
}
}
// 发送方组件
@Component({...})
export class SenderComponent {
constructor(private commService: CommunicationService) {}
sendMessage() {
this.commService.sendMessage('Hello from sender!');
}
}
// 接收方组件
@Component({...})
export class ReceiverComponent implements OnInit, OnDestroy {
message: string = '';
private subscription?: Subscription;
constructor(private commService: CommunicationService) {}
ngOnInit() {
this.subscription = this.commService.message$.subscribe(
message => this.message = message
);
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
}
2. 使用BehaviorSubject
typescript
@Injectable({
providedIn: 'root'
})
export class StateService {
private stateSubject = new BehaviorSubject<AppState>({
user: null,
theme: 'light',
language: 'zh'
});
public state$ = this.stateSubject.asObservable();
updateState(newState: Partial<AppState>) {
const currentState = this.stateSubject.value;
this.stateSubject.next({ ...currentState, ...newState });
}
getCurrentState(): AppState {
return this.stateSubject.value;
}
}
interface AppState {
user: User | null;
theme: string;
language: string;
}
🎮 实践练习
练习1:创建数据管理服务
创建一个完整的数据管理服务,包含:
- CRUD操作
- 数据缓存
- 错误处理
- 加载状态管理
练习2:创建配置服务
创建一个配置管理服务,支持:
- 环境配置
- 动态配置更新
- 配置验证
- 配置持久化
📚 详细示例
完整的数据服务
typescript
// data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
export interface ApiResponse<T> {
data: T;
message: string;
success: boolean;
}
@Injectable({
providedIn: 'root'
})
export class DataService<T> {
private dataSubject = new BehaviorSubject<T[]>([]);
private loadingSubject = new BehaviorSubject<boolean>(false);
private errorSubject = new BehaviorSubject<string | null>(null);
public data$ = this.dataSubject.asObservable();
public loading$ = this.loadingSubject.asObservable();
public error$ = this.errorSubject.asObservable();
private cache = new Map<string, T[]>();
constructor() {}
// 获取数据
getData(endpoint: string, useCache: boolean = true): Observable<T[]> {
if (useCache && this.cache.has(endpoint)) {
this.dataSubject.next(this.cache.get(endpoint)!);
return this.data$;
}
this.setLoading(true);
this.clearError();
// 模拟API调用
return this.simulateApiCall<T[]>(endpoint).pipe(
tap(data => {
this.cache.set(endpoint, data);
this.dataSubject.next(data);
this.setLoading(false);
}),
catchError(error => {
this.setError(error.message);
this.setLoading(false);
return throwError(error);
})
);
}
// 添加数据
addData(endpoint: string, item: T): Observable<T> {
this.setLoading(true);
this.clearError();
return this.simulateApiCall<T>(endpoint, 'POST', item).pipe(
tap(newItem => {
const currentData = this.dataSubject.value;
this.dataSubject.next([...currentData, newItem]);
this.updateCache(endpoint);
this.setLoading(false);
}),
catchError(error => {
this.setError(error.message);
this.setLoading(false);
return throwError(error);
})
);
}
// 更新数据
updateData(endpoint: string, id: number, item: Partial<T>): Observable<T> {
this.setLoading(true);
this.clearError();
return this.simulateApiCall<T>(endpoint, 'PUT', { id, ...item }).pipe(
tap(updatedItem => {
const currentData = this.dataSubject.value;
const index = currentData.findIndex((item: any) => item.id === id);
if (index !== -1) {
currentData[index] = updatedItem;
this.dataSubject.next([...currentData]);
this.updateCache(endpoint);
}
this.setLoading(false);
}),
catchError(error => {
this.setError(error.message);
this.setLoading(false);
return throwError(error);
})
);
}
// 删除数据
deleteData(endpoint: string, id: number): Observable<boolean> {
this.setLoading(true);
this.clearError();
return this.simulateApiCall<boolean>(endpoint, 'DELETE', { id }).pipe(
tap(success => {
if (success) {
const currentData = this.dataSubject.value;
const filteredData = currentData.filter((item: any) => item.id !== id);
this.dataSubject.next(filteredData);
this.updateCache(endpoint);
}
this.setLoading(false);
}),
catchError(error => {
this.setError(error.message);
this.setLoading(false);
return throwError(error);
})
);
}
// 清除缓存
clearCache(endpoint?: string): void {
if (endpoint) {
this.cache.delete(endpoint);
} else {
this.cache.clear();
}
}
// 刷新数据
refreshData(endpoint: string): Observable<T[]> {
this.clearCache(endpoint);
return this.getData(endpoint, false);
}
private setLoading(loading: boolean): void {
this.loadingSubject.next(loading);
}
private setError(error: string): void {
this.errorSubject.next(error);
}
private clearError(): void {
this.errorSubject.next(null);
}
private updateCache(endpoint: string): void {
this.cache.set(endpoint, this.dataSubject.value);
}
// 模拟API调用
private simulateApiCall<R>(
endpoint: string,
method: string = 'GET',
data?: any
): Observable<R> {
return new Observable(observer => {
setTimeout(() => {
// 模拟成功响应
if (Math.random() > 0.1) { // 90% 成功率
observer.next(this.generateMockData<R>(endpoint, method, data));
observer.complete();
} else {
// 模拟错误
observer.error(new Error('API调用失败'));
}
}, 1000); // 模拟网络延迟
});
}
private generateMockData<R>(endpoint: string, method: string, data?: any): R {
// 根据endpoint和method生成模拟数据
// 这里简化处理
return data as R;
}
}
使用数据服务的组件
typescript
// user-list.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { DataService } from '../services/data.service';
interface User {
id: number;
name: string;
email: string;
role: string;
}
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit, OnDestroy {
users: User[] = [];
loading = false;
error: string | null = null;
private subscriptions: Subscription[] = [];
constructor(private dataService: DataService<User>) {}
ngOnInit() {
this.loadUsers();
this.subscribeToData();
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
loadUsers() {
this.dataService.getData('/api/users').subscribe();
}
addUser() {
const newUser: Partial<User> = {
name: '新用户',
email: 'new@example.com',
role: 'user'
};
this.dataService.addData('/api/users', newUser as User).subscribe();
}
updateUser(user: User) {
const updatedUser = { ...user, name: user.name + ' (已更新)' };
this.dataService.updateData('/api/users', user.id, updatedUser).subscribe();
}
deleteUser(user: User) {
if (confirm('确定要删除这个用户吗?')) {
this.dataService.deleteData('/api/users', user.id).subscribe();
}
}
refreshUsers() {
this.dataService.refreshData('/api/users').subscribe();
}
private subscribeToData() {
// 订阅数据变化
this.subscriptions.push(
this.dataService.data$.subscribe(users => {
this.users = users;
})
);
// 订阅加载状态
this.subscriptions.push(
this.dataService.loading$.subscribe(loading => {
this.loading = loading;
})
);
// 订阅错误信息
this.subscriptions.push(
this.dataService.error$.subscribe(error => {
this.error = error;
})
);
}
}
🔧 高级服务模式
1. 工厂服务
typescript
// 工厂服务
@Injectable({
providedIn: 'root'
})
export class LoggerFactory {
createLogger(context: string): Logger {
return new Logger(context);
}
}
// 使用工厂
@Injectable()
export class SomeService {
private logger: Logger;
constructor(private loggerFactory: LoggerFactory) {
this.logger = this.loggerFactory.createLogger('SomeService');
}
}
2. 多提供者
typescript
// 多个实现
export abstract class StorageService {
abstract save(key: string, value: any): void;
abstract get(key: string): any;
}
@Injectable()
export class LocalStorageService extends StorageService {
save(key: string, value: any): void {
localStorage.setItem(key, JSON.stringify(value));
}
get(key: string): any {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
}
}
@Injectable()
export class SessionStorageService extends StorageService {
save(key: string, value: any): void {
sessionStorage.setItem(key, JSON.stringify(value));
}
get(key: string): any {
const item = sessionStorage.getItem(key);
return item ? JSON.parse(item) : null;
}
}
// 在模块中配置
@NgModule({
providers: [
{ provide: StorageService, useClass: LocalStorageService },
// 或者使用工厂
{
provide: StorageService,
useFactory: () => {
return window.localStorage ?
new LocalStorageService() :
new SessionStorageService();
}
}
]
})
export class AppModule {}
✅ 学习检查
完成本章节后,请确认您能够:
- [ ] 理解服务的概念和作用
- [ ] 创建和使用服务
- [ ] 理解依赖注入系统
- [ ] 管理服务作用域
- [ ] 实现服务间通信
- [ ] 使用高级服务模式
🚀 下一步
完成本章节学习后,请继续学习07-HTTP通信。