Ionic性能优化 #

一、性能优化概述 #

1.1 优化维度 #

text
性能优化
    │
    ├── 加载性能
    │   ├── 首屏加载
    │   ├── 路由懒加载
    │   └── 资源压缩
    │
    ├── 运行性能
    │   ├── 虚拟滚动
    │   ├── 变更检测
    │   └── 内存管理
    │
    └── 渲染性能
        ├── CSS优化
        ├── 动画优化
        └── 图片优化

1.2 性能指标 #

指标 目标值 说明
FCP < 1.8s 首次内容绘制
LCP < 2.5s 最大内容绘制
FID < 100ms 首次输入延迟
CLS < 0.1 累积布局偏移
TTI < 3.8s 可交互时间

二、虚拟滚动 #

2.1 基本用法 #

html
<ion-content>
  <ion-virtual-scroll [items]="items" [itemHeight]="56">
    <ion-item *virtualItem="let item">
      <ion-label>{{ item.name }}</ion-label>
    </ion-item>
  </ion-virtual-scroll>
</ion-content>

2.2 复杂列表项 #

typescript
import { Component } from '@angular/core';

interface ListItem {
  id: string;
  name: string;
  avatar: string;
  description: string;
}

@Component({
  selector: 'app-list',
  template: `
    <ion-content>
      <ion-virtual-scroll 
        [items]="items" 
        [itemHeight]="getHeight"
        [headerFn]="getHeader">
        
        <ion-item-divider *virtualHeader="let header">
          {{ header }}
        </ion-item-divider>
        
        <ion-item *virtualItem="let item">
          <ion-avatar slot="start">
            <img [src]="item.avatar">
          </ion-avatar>
          <ion-label>
            <h2>{{ item.name }}</h2>
            <p>{{ item.description }}</p>
          </ion-label>
        </ion-item>
      </ion-virtual-scroll>
    </ion-content>
  `
})
export class ListPage {
  items: ListItem[] = [];
  
  getHeight(item: ListItem): number {
    return item.description ? 80 : 56;
  }
  
  getHeader(item: ListItem): string {
    return item.name.charAt(0).toUpperCase();
  }
}

2.3 无限滚动优化 #

html
<ion-content>
  <ion-virtual-scroll [items]="items" [itemHeight]="56">
    <ion-item *virtualItem="let item">
      {{ item.name }}
    </ion-item>
  </ion-virtual-scroll>
  
  <ion-infinite-scroll (ionInfinite)="loadMore($event)">
    <ion-infinite-scroll-content></ion-infinite-scroll-content>
  </ion-infinite-scroll>
</ion-content>
typescript
loadMore(event: any) {
  this.page++;
  this.api.getItems(this.page).subscribe(items => {
    this.items = [...this.items, ...items];
    event.target.complete();
    
    if (items.length === 0) {
      event.target.disabled = true;
    }
  });
}

三、懒加载 #

3.1 路由懒加载 #

typescript
const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then(m => m.HomePageModule)
  },
  {
    path: 'detail/:id',
    loadChildren: () => import('./detail/detail.module').then(m => m.DetailPageModule)
  }
];

3.2 组件懒加载 #

typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  template: `
    <ng-container *ngIf="showDetail">
      <app-detail></app-detail>
    </ng-container>
  `
})
export class HomePage {
  showDetail = false;
  
  async loadDetail() {
    this.showDetail = true;
  }
}

3.3 图片懒加载 #

html
<ion-img [src]="imageUrl" alt="图片"></ion-img>
typescript
// 自定义懒加载指令
import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[lazyLoad]'
})
export class LazyLoadDirective implements OnInit {
  constructor(private el: ElementRef) {}
  
  ngOnInit() {
    const img = this.el.nativeElement;
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          img.src = img.dataset.src;
          observer.unobserve(img);
        }
      });
    });
    
    observer.observe(img);
  }
}

四、变更检测优化 #

4.1 OnPush策略 #

typescript
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-item',
  templateUrl: 'item.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemComponent {
  @Input() item: any;
}

4.2 手动触发变更检测 #

typescript
import { Component, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor(private cdr: ChangeDetectorRef) {}
  
  updateData() {
    this.data = newData;
    this.cdr.detectChanges();
  }
  
  // 在异步操作后
  async loadData() {
    this.data = await this.api.getData();
    this.cdr.markForCheck();
  }
}

4.3 不可变数据 #

typescript
// 使用不可变数据
this.items = [...this.items, newItem];

// 而不是
this.items.push(newItem);

五、内存管理 #

5.1 清理订阅 #

typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage implements OnInit, OnDestroy {
  private destroy$ = new Subject<void>();
  
  ngOnInit() {
    this.data$.pipe(
      takeUntil(this.destroy$)
    ).subscribe(data => {
      this.data = data;
    });
  }
  
  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

5.2 清理事件监听 #

typescript
import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage implements OnInit, OnDestroy {
  private resizeHandler: () => void;
  
  ngOnInit() {
    this.resizeHandler = () => this.onResize();
    window.addEventListener('resize', this.resizeHandler);
  }
  
  ngOnDestroy() {
    window.removeEventListener('resize', this.resizeHandler);
  }
  
  onResize() {
    // 处理窗口大小变化
  }
}

5.3 清理定时器 #

typescript
import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage implements OnInit, OnDestroy {
  private timer: any;
  
  ngOnInit() {
    this.timer = setInterval(() => {
      this.updateTime();
    }, 1000);
  }
  
  ngOnDestroy() {
    if (this.timer) {
      clearInterval(this.timer);
    }
  }
  
  updateTime() {
    // 更新时间
  }
}

六、打包优化 #

6.1 代码分割 #

typescript
// angular.json
{
  "projects": {
    "app": {
      "architect": {
        "build": {
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        }
      }
    }
  }
}

6.2 Tree Shaking #

typescript
// 只导入需要的模块
import { IonicModule } from '@ionic/angular';

// 而不是
// import * as Ionic from '@ionic/angular';

6.3 压缩优化 #

bash
# 生产构建
ionic build --prod

# 或
ng build --prod --aot --build-optimizer

七、运行时优化 #

7.1 防抖与节流 #

typescript
import { Component } from '@angular/core';
import { debounceTime, throttleTime } from 'rxjs/operators';

@Component({
  selector: 'app-search',
  templateUrl: 'search.page.html'
})
export class SearchPage {
  // 防抖
  onSearch(event: any) {
    fromEvent(event.target, 'input').pipe(
      debounceTime(300)
    ).subscribe(value => {
      this.search(value);
    });
  }
  
  // 节流
  onScroll(event: any) {
    fromEvent(event.target, 'scroll').pipe(
      throttleTime(100)
    ).subscribe(() => {
      this.handleScroll();
    });
  }
}

7.2 Web Worker #

typescript
// worker.ts
self.onmessage = (event) => {
  const result = heavyComputation(event.data);
  self.postMessage(result);
};

function heavyComputation(data: any): any {
  // 执行耗时计算
  return result;
}

// 组件中使用
const worker = new Worker('./worker.ts', { type: 'module' });
worker.postMessage(data);
worker.onmessage = (event) => {
  this.result = event.data;
};

7.3 请求优化 #

typescript
// 缓存请求结果
private cache = new Map<string, Observable<any>>();

getData(key: string): Observable<any> {
  if (this.cache.has(key)) {
    return this.cache.get(key)!;
  }
  
  const request = this.http.get(`/api/data/${key}`).pipe(
    shareReplay(1)
  );
  
  this.cache.set(key, request);
  return request;
}

八、性能监控 #

8.1 Angular性能监控 #

typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor() {
    // 开发模式下启用性能监控
    if (!environment.production) {
      this.enablePerformanceMonitoring();
    }
  }
  
  private enablePerformanceMonitoring() {
    // 监控变更检测
    console.time('changeDetection');
    // ...
    console.timeEnd('changeDetection');
  }
}

8.2 Lighthouse审计 #

bash
# 安装Lighthouse
npm install -g lighthouse

# 运行审计
lighthouse https://your-app.com --view

九、最佳实践总结 #

9.1 性能检查清单 #

  • [ ] 使用虚拟滚动处理长列表
  • [ ] 路由懒加载
  • [ ] 图片懒加载
  • [ ] 使用OnPush变更检测
  • [ ] 清理订阅和事件监听
  • [ ] 代码分割和Tree Shaking
  • [ ] 启用生产模式优化
  • [ ] 使用防抖和节流
  • [ ] 缓存HTTP请求
  • [ ] 性能监控

9.2 下一步 #

掌握了性能优化后,接下来让我们学习 测试策略,了解Ionic应用的测试方法!

最后更新:2026-03-28