跳转至

Angular 深度实践

Angular 深度实践

📚 章节目标

本章节将深入讲解 Angular 框架的核心概念和高级特性,包括依赖注入、 RxJS 响应式编程、 NgRx 状态管理、模块化架构、性能优化等,帮助学习者掌握 Angular 框架的深度应用。

学习目标

  1. 深入理解 Angular 依赖注入系统
  2. 掌握 RxJS 响应式编程
  3. 熟练使用 NgRx 进行状态管理
  4. 掌握 Angular 模块化架构
  5. 理解 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 状态管理、模块化架构、性能优化等。关键要点:

  1. 依赖注入:掌握 Angular 的依赖注入系统和高级用法
  2. RxJS:掌握响应式编程和操作符使用
  3. NgRx:掌握状态管理的最佳实践
  4. 模块化:掌握 Angular 的模块化架构设计
  5. 性能优化:掌握 Angular 性能优化的各种技巧

下一步将深入学习前端状态管理的各种方案。