1. OnPush 变更检测策略
默认变更检测(Default)在每次异步操作后会检查整棵组件树。OnPush 策略让组件只在以下情况才检查:
- 组件的
@Input()引用发生变化(不是内容变化) - 组件内触发了事件(click 等)
- 使用
async管道的 Observable 发出新值 - 手动调用
ChangeDetectorRef.markForCheck() - 组件内 Signal 发生变化(Angular 17+)
import { Component, ChangeDetectionStrategy, input, computed } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush, // 开启 OnPush
template: `
<div class="card">
<h3>{{ user().name }}</h3>
<p>{{ user().email }}</p>
<span [class]="roleClass()">{{ user().role }}</span>
</div>
`
})
export class UserCardComponent {
user = input.required<User>(); // Signal Input(Angular 17+)
// computed 自动跟踪 user() 的变化
roleClass = computed(() =>
`role-badge role-${this.user().role}`
);
}
最佳实践:所有"展示型"组件(只接收数据、不管理状态)都应使用 OnPush。配合 Signal inputs 和 computed,可以获得接近 Signals 级别的精细更新效果。
2. trackBy — 优化 @for 列表
在新控制流 @for 中,track 表达式(对应旧版的 trackBy)告诉 Angular 如何识别列表中的每个元素。如果没有 track,列表项每次都会被销毁重建,带来严重性能问题。
<!-- 新语法(Angular 17+):track 是必填的 -->
@for (user of users(); track user.id) {
<app-user-card [user]="user" />
}
<!-- 旧语法对比:trackBy 是可选的(但推荐) -->
<app-user-card
*ngFor="let user of users; trackBy: trackById"
[user]="user"
/>
<!-- track 的值可以是任意唯一标识 -->
@for (item of items(); track item.id) { ... } <!-- 用 id -->
@for (item of items(); track item.name) { ... } <!-- 用 name -->
@for (item of items(); track $index) { ... } <!-- 用索引(不推荐用于可变列表)-->
3. 懒加载与预加载策略
import { PreloadAllModules, provideRouter, withPreloading } from '@angular/router';
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
routes,
withPreloading(PreloadAllModules) // 空闲时预加载所有懒加载模块
)
]
};
// 自定义预加载策略:只预加载标记了 preload: true 的路由
import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
return route.data?.['preload'] === true ? load() : of(null);
}
}
// 路由配置中使用
{ path: 'dashboard', data: { preload: true }, loadComponent: () => ... }
4. Angular SSR(Server-Side Rendering)
Angular 17 对 SSR 架构进行了重大重写,基于全新的 Node.js 服务端渲染机制,构建更快、配置更简单。
启用 SSR
# 创建项目时启用 SSR
ng new my-app --ssr
# 为现有项目添加 SSR
ng add @angular/ssr
SSR 项目结构
src/
├── app/
│ ├── app.config.ts # 浏览器端配置
│ └── app.config.server.ts # 服务端配置(合并浏览器配置)
├── main.ts # 浏览器入口
└── main.server.ts # 服务端入口
server.ts # Express 服务器
// app.config.server.ts
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';
const serverConfig: ApplicationConfig = {
providers: [provideServerRendering()]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
SSR 中避免浏览器 API
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';
export class StorageService {
private platformId = inject(PLATFORM_ID);
getItem(key: string): string | null {
if (isPlatformBrowser(this.platformId)) {
return localStorage.getItem(key); // 只在浏览器中访问
}
return null;
}
}
5. SSG — 静态生成
# angular.json 中配置 SSG 预渲染
"prerender": {
"builder": "@angular-devkit/build-angular:prerender",
"options": {
"routes": ["/", "/about", "/blog"] // 要预渲染的路由
}
}
# 构建并预渲染
ng build --configuration production
6. 生产构建优化
# 生产构建(自动启用:AOT、Tree Shaking、压缩、代码分割)
ng build --configuration production
# 构建产物位于 dist/my-app/browser/
# 典型输出:
# main.js ~200KB → gzip 后 ~60KB
# polyfills.js ~20KB
# chunk-xxx.js 懒加载模块
# styles.css 全局样式
Bundle 分析
# 安装分析工具
npm install --save-dev webpack-bundle-analyzer
# 生成 stats.json
ng build --stats-json
# 分析
npx webpack-bundle-analyzer dist/my-app/browser/stats.json
7. 部署方案
方案一:Nginx 静态部署(CSR)
# Dockerfile
FROM nginx:alpine
COPY dist/my-app/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
# nginx.conf — 支持 HTML5 pushState 路由
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# 所有路由都返回 index.html(SPA 关键配置)
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 开启 gzip 压缩
gzip on;
gzip_types text/plain application/javascript text/css;
}
方案二:Docker + SSR
# Dockerfile(含 SSR)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 4000
CMD ["node", "dist/my-app/server/main.server.mjs"]
方案三:Vercel 部署(推荐用于个人/团队)
# 安装 Vercel CLI
npm install -g vercel
# 登录并部署
vercel
# vercel.json 配置(CSR 应用)
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
8. 性能优化总结
OnPush + Signals
减少不必要的变更检测,最有效的运行时优化手段
懒加载路由
loadComponent/loadChildren 减小初始包体积
@defer 延迟渲染
非首屏内容按需加载,提升 LCP 指标
SSR/SSG
服务端渲染提升首屏速度,对 SEO 友好
NgOptimizedImage
内置图片指令,自动优化 src/srcset、懒加载
Bundle 分析
webpack-bundle-analyzer 找出体积大的依赖
课程总结:恭喜完成 Angular 18+ 完整教程!从组件/DI/路由/HTTP/表单的核心基础,到 RxJS/Signals 的响应式编程,再到测试和生产部署,你已掌握企业级 Angular 开发的完整知识体系。Angular 的强类型和约束性使大型团队代码更易维护——这正是它在企业应用中长盛不衰的原因。