Angular性能优化技巧

性能优化是Angular应用开发中至关重要的环节。本章将深入讲解各种性能优化技巧,帮助你构建快速、响应灵敏的应用。

性能指标: 首次内容渲染 (FCP) 最大内容绘制 (LCP) 首次输入延迟 (FID) 累计布局偏移 (CLS)

1. 变更检测优化

使用OnPush变更检测策略

将组件的变更检测策略改为OnPush,只有在输入属性发生变化或事件触发时才进行检查。

// 使用OnPush策略
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserProfileComponent {
  // 组件代码
}

使用纯管道

纯管道只会在输入值发生纯变更时执行变换,避免不必要的重新计算。

// 纯管道示例
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'currencyFormat',
  pure: true // 默认就是true,可以省略
})
export class CurrencyFormatPipe implements PipeTransform {
  transform(value: number): string {
    return `$${value.toFixed(2)}`;
  }
}

避免频繁触发变更检测

不要在组件中频繁调用markForCheck或detectChanges。

// 错误示例 - 频繁触发变更检测
export class BadComponent {
  constructor(private cdRef: ChangeDetectorRef) {}

  updateData() {
    // 避免在循环或频繁调用的函数中这样做
    setInterval(() => {
      this.cdRef.markForCheck(); // 太频繁了!
    }, 100);
  }
}

// 正确示例 - 使用rxjs的节流
export class GoodComponent {
  private data$ = interval(100).pipe(
    throttleTime(1000), // 每秒最多更新一次
    map(() => this.getNewData())
  );
}

2. 懒加载和预加载

模块懒加载

将应用拆分为多个模块,按需加载,减少初始包大小。

// 路由配置中的懒加载
const routes: Routes = [
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module')
      .then(m => m.AdminModule)
  },
  {
    path: 'dashboard',
    loadChildren: () => import('./dashboard/dashboard.module')
      .then(m => m.DashboardModule)
  },
  {
    path: 'reports',
    loadChildren: () => import('./reports/reports.module')
      .then(m => m.ReportsModule)
  }
];

预加载策略

使用预加载策略在空闲时加载后续模块。

// 自定义预加载策略
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

export class CustomPreloadStrategy implements PreloadingStrategy {
  preload(route: Route, load: () => Observable): Observable {
    if (route.data && route.data['preload']) {
      return load();
    }
    return of(null);
  }
}

// 在路由中使用
const routes: Routes = [
  {
    path: 'settings',
    loadChildren: () => import('./settings/settings.module')
      .then(m => m.SettingsModule),
    data: { preload: true } // 标记为需要预加载
  }
];

// 在AppModule中提供策略
@NgModule({
  imports: [RouterModule.forRoot(routes, {
    preloadingStrategy: CustomPreloadStrategy
  })],
  providers: [CustomPreloadStrategy]
})
懒加载 vs 预加载性能对比
无懒加载
懒加载
预加载

初始加载时间对比(数值越小越好)

3. 打包优化

生产环境构建

使用生产环境构建配置,启用各种优化。

# 生产环境构建命令
ng build --prod

# 更详细的构建配置
ng build --prod --aot --build-optimizer --optimization

代码分割

使用动态导入进行代码分割。

// 动态导入组件
@Component({
  template: `<ng-container *ngComponentOutlet="currentComponent"></ng-container>`
})
export class LazyComponentLoader {
  currentComponent: any;

  async loadComponent(componentName: string) {
    switch(componentName) {
      case 'chart':
        const { ChartComponent } = await import('./chart/chart.component');
        this.currentComponent = ChartComponent;
        break;
      case 'table':
        const { TableComponent } = await import('./table/table.component');
        this.currentComponent = TableComponent;
        break;
    }
  }
}

移除未使用代码

使用Tree Shaking移除未使用的代码。

// angular.json配置
{
  "projects": {
    "my-app": {
      "architect": {
        "build": {
          "configurations": {
            "production": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb",
                  "maximumError": "10kb"
                }
              ]
            }
          }
        }
      }
    }
  }
}

4. 内存管理

及时取消订阅

避免内存泄漏,及时取消rxjs订阅。

// 使用takeUntil模式取消订阅
export class SafeComponent implements OnDestroy {
  private destroy$ = new Subject();

  constructor() {
    // 自动在组件销毁时取消订阅
    interval(1000)
      .pipe(takeUntil(this.destroy$))
      .subscribe(value => {
        console.log(value);
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

// 使用async管道(推荐)
export class AsyncPipeComponent {
  data$ = this.dataService.getData().pipe(
    shareReplay(1) // 缓存结果
  );
}

// 在模板中使用
// {{ data$ | async }}

使用trackBy函数

在ngFor中使用trackBy函数,避免不必要的DOM操作。

<!-- 使用trackBy函数 -->
<div *ngFor="let item of items; trackBy: trackByFn">
  {{item.name}}
</div>
// 组件中的trackBy函数
trackByFn(index: number, item: any): number {
  return item.id; // 使用唯一标识符
}

避免内存泄漏的常见错误

// 错误示例 - 未取消订阅
export class LeakyComponent {
  constructor() {
    // 错误:未保存订阅引用
    interval(1000).subscribe(value => {
      console.log(value);
    });

    // 错误:未清理定时器
    setInterval(() => {
      this.update();
    }, 1000);
  }
}

// 正确示例
export class CleanComponent implements OnDestroy {
  private subscription: Subscription;
  private intervalId: any;

  constructor() {
    // 保存订阅引用
    this.subscription = interval(1000).subscribe(value => {
      console.log(value);
    });

    // 保存定时器ID
    this.intervalId = setInterval(() => {
      this.update();
    }, 1000);
  }

  ngOnDestroy() {
    // 清理所有资源
    this.subscription.unsubscribe();
    clearInterval(this.intervalId);
  }
}

5. 渲染优化

虚拟滚动

对于大型列表,使用虚拟滚动减少DOM节点数量。

<!-- 使用CDK虚拟滚动 -->
<cdk-virtual-scroll-viewport itemSize="50" class="viewport">
  <div *cdkVirtualFor="let item of items" class="item">
    {{item.name}}
  </div>
</cdk-virtual-scroll-viewport>
// 安装CDK
ng add @angular/cdk

// 在模块中导入
import { ScrollingModule } from '@angular/cdk/scrolling';

@NgModule({
  imports: [ScrollingModule]
})

使用Web Workers

将CPU密集型任务移入Web Worker,避免阻塞UI线程。

// 创建Web Worker
ng generate web-worker app

// 在组件中使用
export class WorkerComponent {
  constructor() {
    if (typeof Worker !== 'undefined') {
      const worker = new Worker('./app.worker', { type: 'module' });
      worker.onmessage = ({ data }) => {
        console.log('收到worker消息:', data);
      };
      worker.postMessage('开始计算');
    }
  }
}

图片优化

延迟加载图片,优化图片资源。

<!-- 使用懒加载图片 -->
<img [src]="imageSrc" loading="lazy" alt="描述文本">

<!-- 响应式图片 -->
<img [srcset]="imageSrcSet" sizes="(max-width: 600px) 100vw, 50vw">

<!-- 使用picture元素 -->
<picture>
  <source media="(max-width: 768px)" [srcset]="mobileImage">
  <source media="(min-width: 769px)" [srcset]="desktopImage">
  <img [src]="fallbackImage" alt="描述">
</picture>

6. 服务端渲染 (SSR)

Angular Universal

使用Angular Universal实现服务端渲染,提升首屏加载速度。

# 添加Universal支持
ng add @nguniversal/express-engine
// 服务端渲染注意事项
export class AppComponent {
  // 避免在服务端运行浏览器特定代码
  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    if (isPlatformBrowser(this.platformId)) {
      // 只在浏览器中执行的代码
      this.initBrowserSpecificFeatures();
    }

    if (isPlatformServer(this.platformId)) {
      // 只在服务器中执行的代码
      this.initServerSpecificFeatures();
    }
  }
}

7. 性能监控与分析

性能优化步骤:

  1. 性能测量 - 使用Chrome DevTools的Performance面板
  2. 识别瓶颈 - 分析火焰图,找到耗时操作
  3. 实施优化 - 根据本文技巧进行优化
  4. 验证效果 - 重新测量,对比优化前后
  5. 持续监控 - 在生产环境中监控性能指标

使用Angular DevTools

安装Angular DevTools扩展,分析变更检测和性能。

// 性能测量代码示例
import { enableProdMode } from '@angular/core';

// 在生产环境中启用性能模式
if (environment.production) {
  enableProdMode();
}

// 使用performance API测量
export class PerformanceService {
  measureOperation(name: string, operation: () => void) {
    const start = performance.now();
    operation();
    const end = performance.now();
    console.log(`${name} 耗时: ${end - start}ms`);
  }
}
优化技巧 收益 复杂度 推荐指数
OnPush变更检测 ★★★★★
模块懒加载 ★★★★★
取消订阅管理 ★★★★★
虚拟滚动 极高 ★★★★☆
服务端渲染 极高 ★★★☆☆
Web Workers ★★☆☆☆
性能优化黄金法则
  • 测量优先:在优化前先测量性能,找到真正的瓶颈
  • 渐进优化:先实施高收益、低复杂度的优化
  • 持续监控:在生产环境设置性能监控
  • 用户体验至上:关注用户感知的性能指标
  • 保持代码可维护:不要为了微小优化牺牲代码质量
常见性能陷阱
  • 在模板中调用函数(每次变更检测都会执行)
  • 过度使用双向绑定
  • 未使用trackBy的ngFor循环
  • 未压缩的静态资源
  • 阻塞UI线程的同步操作
  • 未清理的订阅和定时器

8. 工具与资源

Chrome DevTools
  • Performance面板
  • Lighthouse审计
  • Memory面板
  • Coverage面板
Angular工具
  • Angular DevTools
  • Webpack Bundle Analyzer
  • Source Map Explorer
  • Angular CLI Budgets