Angular夯实根基05,Angular 指令初体验:ngIf 与ngFor 的核心使用技巧

Angular 作为前端主流的企业级框架,指令(Directive)是其核心特性之一,而结构型指令更是日常开发中高频使用的工具 —— 它们直接塑造 DOM 结构,决定元素的 “生死” 与 “循环”。其中*ngIf*ngFor作为最基础也最核心的内置结构指令,掌握其使用技巧能大幅提升开发效率,避免常见坑点。本文将从基础用法到进阶技巧,带你吃透这两个指令。

一、先搞懂:什么是结构型指令?

结构型指令的核心作用是修改 DOM 结构,比如添加、删除、替换元素,或重复渲染一组元素。Angular 内置的结构指令都以*开头(语法糖),本质是对<ng-template>的简化封装。

需要注意:一个元素上不能同时使用多个结构型指令(比如不能同时写*ngIf*ngFor),这是 Angular 的核心规则,避免指令执行顺序冲突。

二、*ngIf:条件渲染的正确姿势

*ngIf用于根据条件决定是否渲染元素(而非隐藏),这是它与[hidden]的核心区别 ——*ngIf不满足条件时,元素会从 DOM 中完全移除,而[hidden]只是通过 CSS 隐藏,仍存在于 DOM 中。

1. 基础用法:简单条件判断

最常用的场景是根据布尔值渲染元素:

<!-- 基础写法 -->
<div *ngIf="isShow">满足条件,我会显示</div>

<!-- 反向条件 -->
<div *ngIf="!isUserLogin">未登录,请先登录</div>

对应的组件类:

@Component({
  selector: 'app-ngIf-demo',
  templateUrl: './ngIf-demo.component.html'
})
export class NgIfDemoComponent {
  isShow = true;
  isUserLogin = false;
}

2. 进阶:else 分支与<ng-template>

当需要 “满足条件显示 A,不满足显示 B” 时,可结合else<ng-template>实现:

<!-- 带else的写法 -->
<div *ngIf="hasPermission; else noPermission">
  您有访问权限,可操作
</div>

<!-- ng-template定义else模板 -->
<ng-template #noPermission>
  <div>无访问权限,请联系管理员</div>
</ng-template>

这里#noPermission是模板引用变量,else后直接引用即可。

3. 高级:then-else 多分支

如果需要 “满足条件显示 A,否则显示 B,且可动态切换”,可使用then语法:

<div *ngIf="status; then successTpl else errorTpl"></div>

<ng-template #successTpl>
  <div>操作成功 ✅</div>
</ng-template>

<ng-template #errorTpl>
  <div>操作失败 ❌</div>
</ng-template>

这种写法更灵活,适合多状态切换的场景。

4. 避坑:*ngIf 与 null/undefined 的处理

*ngIf会将0''nullundefinedfalse都判定为 “假”,但实际开发中需注意:

<!-- 错误示例:若list为[],仍会显示(因为[]是真) -->
<div *ngIf="list">列表有数据</div>

<!-- 正确示例:判断数组是否有内容 -->
<div *ngIf="list && list.length > 0">列表有数据</div>

5. 小技巧:利用 * ngIf 提升性能

对于复杂组件(比如包含大量逻辑的表单、图表),用*ngIf替代[hidden],可避免组件初始化和变更检测,提升页面性能:

<!-- 差:组件仍会初始化,只是隐藏 -->
<app-complex-chart [hidden]="!showChart"></app-complex-chart>

<!-- 优:不满足条件时,组件完全不渲染 -->
<app-complex-chart *ngIf="showChart"></app-complex-chart>

三、*ngFor:循环渲染的核心技巧

*ngFor用于遍历数组 / 可迭代对象,重复渲染元素,是列表渲染的核心指令。掌握其进阶用法,能解决大部分循环场景的问题。

1. 基础用法:遍历数组

<!-- 基础遍历 -->
<ul>
  <li *ngFor="let item of userList">
    {{ item.name }} - {{ item.age }}岁
  </li>
</ul>

组件类:

export class NgForDemoComponent {
  userList = [
    { name: '张三', age: 25 },
    { name: '李四', age: 30 },
    { name: '王五', age: 28 }
  ];
}

2. 必加项:trackBy 提升性能

默认情况下,*ngFor会通过对象引用判断元素是否变化,当数组重新赋值(比如接口刷新)时,即使数据内容不变,Angular 也会销毁并重建所有 DOM 元素,性能极低。

解决方案:使用trackBy指定唯一标识

<!-- 带trackBy的遍历 -->
<ul>
  <li *ngFor="let item of userList; trackBy: trackByUserId">
    {{ item.name }} - {{ item.age }}岁
  </li>
</ul>

组件类中定义trackBy方法:

// 以id为唯一标识
trackByUserId(index: number, item: any): number {
  return item.id; // 确保item有唯一的id字段
}

trackBy接收两个参数:index(索引)和item(当前项),返回值必须是唯一的(如 id、手机号等)。

3. 常用变量:index、even、odd、first、last

*ngFor提供了内置变量,满足特殊场景的需求:

变量名含义
index当前项的索引(从 0 开始)
even是否为偶数项(布尔值)
odd是否为奇数项(布尔值)
first是否为第一项(布尔值)
last是否为最后一项(布尔值)

示例:

<table>
  <tr 
    *ngFor="let item of userList; 
             trackBy: trackByUserId;
             let i = index; 
             let isEven = even;
             let isFirst = first;
             let isLast = last"
    [style.backgroundColor]="isEven ? '#f5f5f5' : '#fff'"
  >
    <td>{{ i + 1 }}</td> <!-- 索引从1开始 -->
    <td>{{ item.name }}</td>
    <td>{{ isFirst ? '✅ 第一项' : isLast ? '🔚 最后一项' : '' }}</td>
  </tr>
</table>

4. 嵌套 * ngFor:多维数组遍历

处理二维数组(比如表格的行和列)时,可嵌套*ngFor

<!-- 嵌套循环:遍历行和列 -->
<table border="1">
  <tr *ngFor="let row of tableData; trackBy: trackByRowIndex">
    <td *ngFor="let cell of row; trackBy: trackByCellIndex">
      {{ cell }}
    </td>
  </tr>
</table>

组件类:

tableData = [
  ['姓名', '年龄', '性别'],
  ['张三', 25, '男'],
  ['李四', 30, '女']
];

trackByRowIndex(index: number): number {
  return index;
}

trackByCellIndex(index: number): number {
  return index;
}

5. 避坑:ngFor 与ngIf 的冲突解决

前面提到 “一个元素不能同时用多个结构指令”,如果需要 “循环且条件过滤”,有两种解决方案:

方案 1:外层套<ng-container>(推荐)

<ng-container>是 Angular 的 “空容器”,不会生成实际 DOM,专门用于包裹指令:

<ul>
  <ng-container *ngFor="let item of userList; trackBy: trackByUserId">
    <!-- 只显示年龄大于25的用户 -->
    <li *ngIf="item.age > 25">
      {{ item.name }} - {{ item.age }}岁
    </li>
  </ng-container>
</ul>
方案 2:提前过滤数组(性能更优)

在组件类中先过滤数据,模板中直接遍历过滤后的数组:

// 组件类
get filteredUserList() {
  return this.userList.filter(item => item.age > 25);
}
<!-- 模板 -->
<ul>
  <li *ngFor="let item of filteredUserList; trackBy: trackByUserId">
    {{ item.name }} - {{ item.age }}岁
  </li>
</ul>

这种方式减少了模板中的逻辑,且避免了循环内的条件判断,性能更好。

四、总结:核心要点

  1. *ngIf是 “DOM 级别的条件渲染”,而非隐藏,适合控制元素的存在与否,优先于[hidden]
  2. *ngIfelse/then分支需结合<ng-template>使用,满足多状态切换;
  3. *ngFor必须加trackBy,避免数组更新时 DOM 全量重建,提升性能;
  4. 利用*ngFor的内置变量(index/even/odd 等)解决特殊场景;
  5. 避免在同一元素上混用*ngIf*ngFor,优先用<ng-container>或提前过滤数组。

掌握这两个指令的核心技巧,能解决 Angular 开发中 80% 的 DOM 结构控制场景。后续还可以探索自定义结构指令,进一步扩展 Angular 的 DOM 操作能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

canjun_wen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值