Skip to content

Angular 17 中 *ngFor 与 @for 的区别

问题陈述

随着 Angular 17 的重大更新,开发者开始接触名为**控制流语法(control flow)**的新模板特性。其中,传统的 *ngFor 指令被新的 @for 语法替代,这种变化引发了许多开发者的困惑:

  • @for 是新版的 *ngFor 还是不同的功能?
  • 为什么 Angular 要放弃原有的简洁语法?
  • 两种语法在功能和性能上有何本质区别?

本文将详解 @for*ngFor 的关键差异、优势以及迁移路径。

解决方案

@for 的本质:控制流语法革新

@for 是 Angular 17 引入的全新控制流语法的一部分,旨在替代 *ngFor, *ngIf*ngSwitch 等结构型指令:

html
<!-- 旧版 *ngFor -->
<option *ngFor="let car of cars" [value]="car.value">{{car.viewValue}}</option>

<!-- 新版 @for -->
@for (car of cars; track car.id) {
  <option [value]="car.value">{{car.viewValue}}</option>
}

核心差异与优势

特性*ngFor@for
跟踪机制需手动添加 trackBytrack 表达式强制要求,性能更安全
空状态处理需额外添加 *ngIf原生支持 @empty
导入依赖需显式导入 CommonModule无需导入,自动支持
变更检测依赖默认策略或自定义 trackBy自动优化最小变更操作
代码体积生成较多运行时代码编译后代码更精简

主要优势详解

  1. 强制 Track 提升性能
    @for 要求开发者必须提供 track 表达式,从根本上防止了因缺少跟踪导致的性能问题:

    html
    <!-- track 为强制要求项 -->
    @for (user of users; track user.id) {
      <div>{{ user.name }}</div>
    }

    对比旧版 *ngFor 需手动添加 trackBy 函数:

    html
    <!-- 旧版容易忽略 trackBy -->
    <div *ngFor="let user of users; trackBy: trackById">{{ user.name }}</div>
  2. 内置空状态处理
    直接通过 @empty 块处理集合为空的情况,无需额外逻辑:

    html
    @for (item of items; track item.id) {
      <div>{{ item.name }}</div>
    } @empty {
      <p>列表为空!</p>
    }
  3. 减少依赖与模板代码

    • 独立组件中无需导入任何指令模块
    • 编译后生成更少代码,最终打包体积更小
  4. 开发体验优化
    相比指令语法,@for 更符合现代 JavaScript 的代码风格,提供更清晰的块级作用域

代码示例

基础用法对比

html
<!-- *ngFor 传统写法 -->
<div *ngFor="let product of products">
  {{ product.name }}
</div>

<!-- @for 控制流写法 -->
@for (product of products; track product.id) {
  <div>{{ product.name }}</div>
}

空状态处理

html
<!-- *ngFor 需联合 *ngIf 实现空状态 -->
<ng-container *ngIf="products.length > 0; else empty">
  <div *ngFor="let product of products">
    {{ product.name }}
  </div>
</ng-container>
<ng-template #empty>
  <p>暂无产品</p>
</ng-template>

<!-- @for 原生支持空状态 -->
@for (product of products; track product.id) {
  <div>{{ product.name }}</div>
} @empty {
  <p>暂无产品</p>
}

复杂数据跟踪

html
<!-- 使用对象属性跟踪 -->
@for (user of users; track user.employeeId) {...}

<!-- 使用索引跟踪 -->
@for (log of logs; track $index) {...}

<!-- 复合跟踪表达式 -->
@for (item of list; track item.name + item.type) {...}

迁移指南

手动升级步骤

  1. 将所有 *ngFor 替换为 @for 块语法
  2. 添加必要的 track 表达式
  3. 使用 @empty 替换原有的空状态模板
  4. 删除模板中 CommonModule 的导入(如有)

自动迁移命令

运行 Angular CLI 命令一键转换现有模板:

bash
ng generate @angular/core:control-flow

注意事项

  • track 表达式是强制要求的,迁移时需确认
  • 新语法完全兼容 Angular 17+ 项目
  • 旧语法目前仍可用,但官方推荐采用新控制流

限制说明

@for 不支持 *ngFor 的某些进阶用法:

  • 不能自定义 trackBy 函数实现
  • 不包含 count, index 等隐式变量(通过 $count, $index 访问)

结论

@for 并非是对 *ngFor 的简单重命名,而是 Angular 控制流语法革新的一部分,主要优势为:

  1. 强制跟踪机制防止性能隐患
  2. 精简模板代码提升可读性
  3. 原生空状态处理减少分支逻辑
  4. 编译优化减小最终包体积

尽管 *ngFor 语法更加紧凑,但 @for 通过设计约束带来了更健壮的代码实践。官方文档明确表示该语法是未来方向,建议所有新项目直接采用 @for,并使用迁移命令升级现有项目。

深入阅读:Angular 官方控制流文档