Angular深度实践¶
📚 章节目标¶
本章节将深入讲解Angular框架的核心概念和高级特性,包括依赖注入、RxJS响应式编程、NgRx状态管理、模块化架构、性能优化等,帮助学习者掌握Angular框架的深度应用。
学习目标¶
- 深入理解Angular依赖注入系统
- 掌握RxJS响应式编程
- 熟练使用NgRx进行状态管理
- 掌握Angular模块化架构
- 理解Angular性能优化策略
💉 Angular依赖注入系统¶
1. 依赖注入基础¶
1.1 基本概念¶
TypeScript
// 服务定义
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root', // 在根级别提供服务
})
export class UserService {
private users: User[] = [];
getUsers(): User[] {
return this.users;
}
addUser(user: User): void {
this.users.push(user);
}
}
// 组件中使用服务
import { Component } 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 {
users: User[] = [];
constructor(private userService: UserService) {
this.users = this.userService.getUsers();
}
}
1.2 提供者配置¶
TypeScript
// 在模块中提供服务
import { NgModule } from '@angular/core';
import { UserService } from './user.service';
@NgModule({
declarations: [UserListComponent],
providers: [
UserService, // 简写形式
// 或者
{
provide: UserService,
useClass: UserService,
},
],
})
export class UserModule {}
// 使用useValue提供固定值
@NgModule({
providers: [
{
provide: 'API_URL',
useValue: 'https://api.example.com',
},
],
})
export class AppModule {}
// 使用useFactory动态创建服务
@NgModule({
providers: [
{
provide: UserService,
useFactory: (httpClient: HttpClient) => {
return new UserService(httpClient);
},
deps: [HttpClient],
},
],
})
export class AppModule {}
// 使用useExisting别名
@NgModule({
providers: [
UserService,
{
provide: 'UserServiceAlias',
useExisting: UserService,
},
],
})
export class AppModule {}
2. 高级依赖注入¶
2.1 作用域¶
TypeScript
// 根级别服务(单例)
@Injectable({
providedIn: 'root',
})
export class GlobalService {}
// 模块级别服务
@NgModule({
providers: [ModuleService],
})
export class FeatureModule {}
// 组件级别服务
@Component({
selector: 'app-user',
providers: [ComponentService],
template: `...`,
})
export class UserComponent {
constructor(private service: ComponentService) {}
}
// 使用 providedIn: 'any'
@Injectable({
providedIn: 'any',
})
export class LazyService {} // 在懒加载模块中会创建新实例
2.2 装饰器¶
TypeScript
// @Inject - 注入Token
import { Inject, Injectable } from '@angular/core';
@Injectable()
export class ApiService {
constructor(
@Inject('API_URL') private apiUrl: string,
private http: HttpClient
) {}
getData() {
return this.http.get(this.apiUrl);
}
}
// @Optional - 可选依赖
@Injectable()
export class OptionalService {
constructor(
@Optional() private optionalService?: OptionalDependency
) {}
doSomething() {
if (this.optionalService) {
this.optionalService.method();
}
}
}
// @Self - 只在组件本身查找
@Component({
selector: 'app-example',
providers: [ExampleService],
template: `...`,
})
export class ExampleComponent {
constructor(@Self() private service: ExampleService) {}
}
// @SkipSelf - 跳过自身,从父级查找
@Component({
selector: 'app-child',
template: `...`,
})
export class ChildComponent {
constructor(@SkipSelf() private parentService: ParentService) {}
}
// @Host - 只在宿主元素查找
@Directive({
selector: '[appHighlight]',
})
export class HighlightDirective {
constructor(@Host() private elementRef: ElementRef) {}
}
2.3 InjectionToken¶
TypeScript
// 创建InjectionToken
import { InjectionToken } from '@angular/core';
export const API_URL = new InjectionToken<string>('API_URL');
export const CONFIG = new InjectionToken<AppConfig>('AppConfig');
// 提供值
@NgModule({
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' },
{
provide: CONFIG,
useValue: {
apiUrl: 'https://api.example.com',
timeout: 5000,
},
},
],
})
export class AppModule {}
// 注入Token
@Injectable()
export class ApiService {
constructor(
@Inject(API_URL) private apiUrl: string,
@Inject(CONFIG) private config: AppConfig
) {}
}
3. 自定义装饰器¶
TypeScript
// 创建自定义装饰器
export function Loggable(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${key} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${key} returned:`, result);
return result;
};
return descriptor;
}
// 使用装饰器
@Injectable()
export class UserService {
@Loggable
getUsers(): User[] {
return [];
}
}
📡 RxJS响应式编程¶
1. Observable基础¶
1.1 创建Observable¶
TypeScript
import { Observable } from 'rxjs';
// 创建自定义Observable
const customObservable = new Observable((subscriber) => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete();
});
// 订阅Observable
customObservable.subscribe({
next: (value) => console.log(value),
error: (err) => console.error(err),
complete: () => console.log('Complete'),
});
// 使用of创建Observable
import { of } from 'rxjs';
const numbers$ = of(1, 2, 3, 4, 5);
// 使用from创建Observable
import { from, fromEvent } from 'rxjs';
const array$ = from([1, 2, 3, 4, 5]);
const promise$ = from(fetch('/api/data'));
const event$ = fromEvent(document, 'click');
// 使用interval创建Observable
import { interval } from 'rxjs';
const interval$ = interval(1000); // 每秒发射一个值
// 使用timer创建Observable
import { timer } from 'rxjs';
const timer$ = timer(1000, 500); // 1秒后开始,每500ms发射一个值
1.2 操作符¶
TypeScript
import { of, from, interval } from 'rxjs';
import {
map,
filter,
tap,
take,
debounceTime,
distinctUntilChanged,
switchMap,
mergeMap,
concatMap,
catchError,
} from 'rxjs/operators';
// map - 转换值
of(1, 2, 3).pipe(
map(x => x * 2)
).subscribe(console.log); // 2, 4, 6
// filter - 过滤值
of(1, 2, 3, 4, 5).pipe(
filter(x => x % 2 === 0)
).subscribe(console.log); // 2, 4
// tap - 副作用
of(1, 2, 3).pipe(
tap(x => console.log('Before:', x)),
map(x => x * 2),
tap(x => console.log('After:', x))
).subscribe(console.log);
// take - 只取前n个值
interval(1000).pipe(
take(5)
).subscribe(console.log); // 0, 1, 2, 3, 4
// debounceTime - 防抖
fromEvent(document, 'input').pipe(
debounceTime(300)
).subscribe(console.log);
// distinctUntilChanged - 去重
of(1, 1, 2, 2, 3, 3).pipe(
distinctUntilChanged()
).subscribe(console.log); // 1, 2, 3
// switchMap - 切换映射(取消前一个请求)
searchInput$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(query => this.http.get(`/api/search?q=${query}`))
).subscribe(results => {
this.results = results;
});
// mergeMap - 并发映射
of(1, 2, 3).pipe(
mergeMap(id => this.http.get(`/api/items/${id}`))
).subscribe(console.log);
// concatMap - 顺序映射
of(1, 2, 3).pipe(
concatMap(id => this.http.get(`/api/items/${id}`))
).subscribe(console.log);
// catchError - 错误处理
this.http.get('/api/data').pipe(
catchError(error => {
console.error('Error:', error);
return of([]); // 返回默认值
})
).subscribe(data => {
this.data = data;
});
2. Subject¶
2.1 Subject类型¶
TypeScript
import { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';
// Subject - 多播Observable
const subject = new Subject<number>();
subject.subscribe({
next: (v) => console.log(`observerA: ${v}`)
});
subject.subscribe({
next: (v) => console.log(`observerB: ${v}`)
});
subject.next(1);
subject.next(2);
// BehaviorSubject - 有初始值,新订阅者立即收到最新值
const behaviorSubject = new BehaviorSubject<number>(0);
behaviorSubject.subscribe(v => console.log(`Observer A: ${v}`)); // 0
behaviorSubject.next(1);
behaviorSubject.subscribe(v => console.log(`Observer B: ${v}`)); // 1
// ReplaySubject - 重放指定数量的值
const replaySubject = new ReplaySubject<number>(2);
replaySubject.next(1);
replaySubject.next(2);
replaySubject.next(3);
replaySubject.subscribe(v => console.log(`Observer A: ${v}`)); // 2, 3
// AsyncSubject - 只在complete时发射最后一个值
const asyncSubject = new AsyncSubject<number>();
asyncSubject.subscribe(v => console.log(`Observer A: ${v}`));
asyncSubject.next(1);
asyncSubject.next(2);
asyncSubject.complete(); // 2
2.2 在Angular中使用Subject¶
TypeScript
import { Injectable } from '@angular/core';
import { Subject, BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class MessageService {
// 普通Subject
private messageSubject = new Subject<string>();
message$ = this.messageSubject.asObservable();
// BehaviorSubject
private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();
// 发送消息
sendMessage(message: string) {
this.messageSubject.next(message);
}
// 更新用户
updateUser(user: User) {
this.userSubject.next(user);
}
// 获取当前用户
getCurrentUser(): User | null {
return this.userSubject.value;
}
}
// 组件中使用
@Component({
selector: 'app-message',
template: `
<div *ngIf="user$ | async as user">
Hello, {{ user.name }}!
</div>
`,
})
export class MessageComponent {
user$ = this.messageService.user$;
constructor(private messageService: MessageService) {}
}
3. 在Angular中使用RxJS¶
3.1 HttpClient¶
TypeScript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ApiService {
constructor(private http: HttpClient) {}
// GET请求
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users');
}
// POST请求
createUser(user: User): Observable<User> {
return this.http.post<User>('/api/users', user);
}
// PUT请求
updateUser(user: User): Observable<User> {
return this.http.put<User>(`/api/users/${user.id}`, user);
}
// DELETE请求
deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`/api/users/${id}`);
}
// 带错误处理
getUsersSafe(): Observable<User[]> {
return this.http.get<User[]>('/api/users').pipe(
catchError(error => {
console.error('Error fetching users:', error);
return of([]); // 返回空数组
})
);
}
// 带数据转换
getUserNames(): Observable<string[]> {
return this.http.get<User[]>('/api/users').pipe(
map(users => users.map(user => user.name))
);
}
}
3.2 表单处理¶
TypeScript
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
@Component({
selector: 'app-search',
template: `
<form [formGroup]="searchForm">
<input
formControlName="search"
placeholder="Search..."
/>
</form>
<div *ngIf="results$ | async as results">
<div *ngFor="let result of results">
{{ result.name }}
</div>
</div>
`,
})
export class SearchComponent {
searchForm: FormGroup;
results$: Observable<Result[]>;
constructor(
private fb: FormBuilder,
private searchService: SearchService
) {
this.searchForm = this.fb.group({
search: ['', Validators.required],
});
this.results$ = this.searchForm.get('search')!.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(query => this.searchService.search(query))
);
}
}
🗄️ NgRx状态管理¶
1. NgRx基础¶
1.1 Store结构¶
TypeScript
// 定义状态接口
export interface AppState { // interface定义类型契约
users: UserState;
todos: TodoState;
}
export interface UserState {
users: User[];
loading: boolean;
error: string | null;
}
export interface TodoState {
todos: Todo[];
filter: 'all' | 'active' | 'completed';
}
// 定义Action
import { createAction, props } from '@ngrx/store';
export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction(
'[User] Load Users Success',
props<{ users: User[] }>()
);
export const loadUsersFailure = createAction(
'[User] Load Users Failure',
props<{ error: string }>()
);
export const addUser = createAction(
'[User] Add User',
props<{ user: User }>()
);
export const updateUser = createAction(
'[User] Update User',
props<{ user: User }>()
);
export const deleteUser = createAction(
'[User] Delete User',
props<{ id: number }>()
);
1.2 Reducer¶
TypeScript
import { createReducer, on } from '@ngrx/store';
import {
loadUsers,
loadUsersSuccess,
loadUsersFailure,
addUser,
updateUser,
deleteUser,
} from './user.actions';
export const initialState: UserState = {
users: [],
loading: false,
error: null,
};
export const userReducer = createReducer(
initialState,
on(loadUsers, (state) => ({
...state,
loading: true,
error: null,
})),
on(loadUsersSuccess, (state, { users }) => ({
...state,
users,
loading: false,
})),
on(loadUsersFailure, (state, { error }) => ({
...state,
loading: false,
error,
})),
on(addUser, (state, { user }) => ({
...state,
users: [...state.users, user],
})),
on(updateUser, (state, { user }) => ({
...state,
users: state.users.map(u => u.id === user.id ? user : u),
})),
on(deleteUser, (state, { id }) => ({
...state,
users: state.users.filter(u => u.id !== id),
}))
);
1.3 Effects¶
TypeScript
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { UserService } from '../services/user.service';
import {
loadUsers,
loadUsersSuccess,
loadUsersFailure,
addUserSuccess,
updateUserSuccess,
deleteUserSuccess,
} from './user.actions';
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(loadUsers),
mergeMap(() =>
this.userService.getUsers().pipe(
map(users => loadUsersSuccess({ users })),
catchError(error => of(loadUsersFailure({ error: error.message })))
)
)
)
);
addUser$ = createEffect(() =>
this.actions$.pipe(
ofType(addUser),
mergeMap(({ user }) =>
this.userService.createUser(user).pipe(
map(newUser => addUserSuccess({ user: newUser })),
catchError(error => of(loadUsersFailure({ error: error.message })))
)
)
)
);
updateUser$ = createEffect(() =>
this.actions$.pipe(
ofType(updateUser),
mergeMap(({ user }) =>
this.userService.updateUser(user).pipe(
map(updatedUser => updateUserSuccess({ user: updatedUser })),
catchError(error => of(loadUsersFailure({ error: error.message })))
)
)
)
);
deleteUser$ = createEffect(() =>
this.actions$.pipe(
ofType(deleteUser),
mergeMap(({ id }) =>
this.userService.deleteUser(id).pipe(
map(() => deleteUserSuccess({ id })),
catchError(error => of(loadUsersFailure({ error: error.message })))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
1.4 Selectors¶
TypeScript
import { createFeatureSelector, createSelector } from '@ngrx/store';
// Feature selector
export const selectUserState = createFeatureSelector<UserState>('users');
// Simple selectors
export const selectUsers = createSelector(
selectUserState,
(state) => state.users
);
export const selectUserLoading = createSelector(
selectUserState,
(state) => state.loading
);
export const selectUserError = createSelector(
selectUserState,
(state) => state.error
);
// Composed selectors
export const selectUserById = (id: number) =>
createSelector(
selectUsers,
(users) => users.find(user => user.id === id)
);
export const selectActiveUsers = createSelector(
selectUsers,
(users) => users.filter(user => user.active)
);
export const selectUserCount = createSelector(
selectUsers,
(users) => users.length
);
2. 在组件中使用NgRx¶
2.1 选择数据¶
TypeScript
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { loadUsers, addUser, updateUser, deleteUser } from './store/user.actions';
import {
selectUsers,
selectUserLoading,
selectUserError,
} from './store/user.selectors';
@Component({
selector: 'app-user-list',
template: `
<div *ngIf="loading$ | async">Loading...</div>
<div *ngIf="error$ | async as error" class="error">
Error: {{ error }}
</div>
<ul>
<li *ngFor="let user of users$ | async">
{{ user.name }}
<button (click)="updateUser(user)">Edit</button>
<button (click)="deleteUser(user.id)">Delete</button>
</li>
</ul>
<button (click)="addNewUser()">Add User</button>
`,
})
export class UserListComponent implements OnInit {
users$: Observable<User[]>;
loading$: Observable<boolean>;
error$: Observable<string | null>;
constructor(private store: Store) {
this.users$ = this.store.select(selectUsers);
this.loading$ = this.store.select(selectUserLoading);
this.error$ = this.store.select(selectUserError);
}
ngOnInit() {
this.store.dispatch(loadUsers());
}
addNewUser() {
const newUser: User = {
id: Date.now(),
name: 'New User',
email: 'new@example.com',
};
this.store.dispatch(addUser({ user: newUser }));
}
updateUser(user: User) {
const updatedUser = { ...user, name: 'Updated Name' };
this.store.dispatch(updateUser({ user: updatedUser }));
}
deleteUser(id: number) {
this.store.dispatch(deleteUser({ id }));
}
}
2.2 表单集成¶
TypeScript
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { searchUsers } from './store/user.actions';
@Component({
selector: 'app-user-search',
template: `
<form [formGroup]="searchForm">
<input
formControlName="query"
placeholder="Search users..."
/>
</form>
`,
})
export class UserSearchComponent {
searchForm: FormGroup;
constructor(
private fb: FormBuilder,
private store: Store
) {
this.searchForm = this.fb.group({
query: [''],
});
this.searchForm.get('query')!.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged()
).subscribe(query => {
this.store.dispatch(searchUsers({ query }));
});
}
}
🏗️ Angular模块化架构¶
1. 模块系统¶
1.1 模块类型¶
TypeScript
// 根模块
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
// 功能模块
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserListComponent } from './user-list.component';
import { UserDetailComponent } from './user-detail.component';
import { UserService } from './user.service';
@NgModule({
declarations: [
UserListComponent,
UserDetailComponent,
],
imports: [
CommonModule,
],
providers: [UserService],
exports: [
UserListComponent,
UserDetailComponent,
],
})
export class UserModule {}
// 路由模块
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
component: UserListComponent,
},
{
path: ':id',
component: UserDetailComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class UserRoutingModule {}
1.2 懒加载模块¶
TypeScript
// 根路由配置
const routes: Routes = [
{
path: '',
redirectTo: '/home',
pathMatch: 'full',
},
{
path: 'home',
loadChildren: () =>
import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'users',
loadChildren: () =>
import('./user/user.module').then(m => m.UserModule),
},
];
// 懒加载模块
@NgModule({
declarations: [
UserListComponent,
UserDetailComponent,
],
imports: [
CommonModule,
UserRoutingModule,
],
})
export class UserModule {}
2. 共享模块¶
TypeScript
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { ReactiveFormsModule } from '@angular/forms';
import { ButtonComponent } from './button.component';
import { InputComponent } from './input.component';
@NgModule({
declarations: [
ButtonComponent,
InputComponent,
],
imports: [
CommonModule,
MatButtonModule,
MatInputModule,
ReactiveFormsModule,
],
exports: [
CommonModule,
MatButtonModule,
MatInputModule,
ReactiveFormsModule,
ButtonComponent,
InputComponent,
],
})
export class SharedModule {}
3. 核心模块¶
TypeScript
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import { ApiService } from './api.service';
@NgModule({
imports: [HttpClientModule],
providers: [
AuthService,
UserService,
ApiService,
],
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error('CoreModule is already loaded. Import it in the AppModule only');
}
}
}
⚡ Angular性能优化¶
1. 变更检测优化¶
1.1 OnPush策略¶
TypeScript
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div>
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserCardComponent {
@Input() user!: User;
}
1.2 手动触发变更检测¶
TypeScript
import { Component, ChangeDetectorRef, OnInit } from '@angular/core';
@Component({
selector: 'app-timer',
template: `
<div>Time: {{ time }}</div>
`,
})
export class TimerComponent implements OnInit {
time = 0;
constructor(private cdr: ChangeDetectorRef) {}
ngOnInit() {
setInterval(() => {
this.time++;
this.cdr.markForCheck(); // 手动触发变更检测
}, 1000);
}
}
2. 优化列表渲染¶
2.1 trackBy函数¶
TypeScript
@Component({
selector: 'app-user-list',
template: `
<ul>
<li *ngFor="let user of users; trackBy: trackByUserId">
{{ user.name }}
</li>
</ul>
`,
})
export class UserListComponent {
users: User[] = [];
trackByUserId(index: number, user: User): number {
return user.id;
}
}
2.2 虚拟滚动¶
TypeScript
import { Component } from '@angular/core';
import { ScrollingModule } from '@angular/cdk/scrolling';
@Component({
selector: 'app-virtual-list',
template: `
<cdk-virtual-scroll-viewport itemSize="50" class="example-viewport">
<div *cdkVirtualFor="let item of items" class="example-item">
{{ item }}
</div>
</cdk-virtual-scroll-viewport>
`,
styles: [`
.example-viewport {
height: 400px;
width: 100%;
}
.example-item {
height: 50px;
}
`],
})
export class VirtualListComponent {
items = Array.from({ length: 100000 }, (_, i) => `Item ${i}`);
}
3. 异步加载¶
3.1 预加载策略¶
TypeScript
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: 'home',
loadChildren: () =>
import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'users',
loadChildren: () =>
import('./user/user.module').then(m => m.UserModule),
data: { preload: true }, // 自定义预加载标记
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: CustomPreloadingStrategy,
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}
3.2 自定义预加载策略¶
TypeScript
import { Injectable } from '@angular/core';
import {
PreloadingStrategy,
Route,
Router,
} from '@angular/router';
import { Observable, of, timer } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (route.data && route.data['preload']) {
// 延迟5秒后预加载
return timer(5000).pipe(mergeMap(() => load()));
}
return of(null);
}
}
📝 练习题¶
1. 基础题¶
题目1:实现一个简单的依赖注入服务
TypeScript
// 创建一个LoggerService
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class LoggerService {
// 实现日志方法
// log(message: string)
// error(message: string)
// warn(message: string)
}
// 在组件中使用
@Component({
selector: 'app-example',
template: `<button (click)="logMessage()">Log</button>`,
})
export class ExampleComponent {
constructor(private logger: LoggerService) {}
logMessage() {
this.logger.log('Button clicked');
}
}
2. 进阶题¶
题目2:使用RxJS实现一个搜索功能
TypeScript
// 实现搜索组件
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
@Component({
selector: 'app-search',
template: `
<input [formControl]="searchControl" placeholder="Search...">
<div *ngFor="let result of results">
{{ result.name }}
</div>
`,
})
export class SearchComponent {
searchControl = new FormControl();
results: SearchResult[] = [];
constructor(private searchService: SearchService) {
// 实现搜索逻辑
// 1. 监听输入变化
// 2. 防抖300ms
// 3. 去重
// 4. 发送搜索请求
}
}
3. 面试题¶
题目3:解释Angular的依赖注入原理
TypeScript
// 答案要点:
// 1. Angular使用装饰器(@Injectable, @Inject, @Optional等)标记依赖
// 2. 通过反射API获取构造函数参数
// 3. 根据Provider配置创建实例
// 4. 维护依赖注入树
// 5. 支持单例、多例等不同作用域
// 简化的依赖注入实现
class Injector {
private providers = new Map();
constructor(providers: any[]) {
providers.forEach(provider => { // 箭头函数:简洁的函数语法
this.providers.set(provider.provide, provider);
});
}
get(token: any) {
const provider = this.providers.get(token); // const不可重新赋值;let块级作用域变量
if (!provider) {
throw new Error(`No provider for ${token}`);
}
if (provider.useClass) {
return this.createInstance(provider.useClass);
}
if (provider.useValue) {
return provider.useValue;
}
if (provider.useFactory) {
const deps = provider.deps.map((dep: any) => this.get(dep)); // map转换每个元素;filter筛选;reduce累积
return provider.useFactory(...deps); // ...展开运算符:展开数组/对象
}
}
private createInstance(Class: any) {
const paramTypes = Reflect.getMetadata('design:paramtypes', Class) || [];
const deps = paramTypes.map((dep: any) => this.get(dep));
return new Class(...deps);
}
}
🎯 本章总结¶
本章节深入讲解了Angular框架的核心概念和高级特性,包括依赖注入、RxJS响应式编程、NgRx状态管理、模块化架构、性能优化等。关键要点:
- 依赖注入:掌握Angular的依赖注入系统和高级用法
- RxJS:掌握响应式编程和操作符使用
- NgRx:掌握状态管理的最佳实践
- 模块化:掌握Angular的模块化架构设计
- 性能优化:掌握Angular性能优化的各种技巧
下一步将深入学习前端状态管理的各种方案。