跳转至

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性能优化的各种技巧

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