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、''、null、undefined、false都判定为 “假”,但实际开发中需注意:
<!-- 错误示例:若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>
这种方式减少了模板中的逻辑,且避免了循环内的条件判断,性能更好。
四、总结:核心要点
*ngIf是 “DOM 级别的条件渲染”,而非隐藏,适合控制元素的存在与否,优先于[hidden];*ngIf的else/then分支需结合<ng-template>使用,满足多状态切换;*ngFor必须加trackBy,避免数组更新时 DOM 全量重建,提升性能;- 利用
*ngFor的内置变量(index/even/odd 等)解决特殊场景; - 避免在同一元素上混用
*ngIf和*ngFor,优先用<ng-container>或提前过滤数组。
掌握这两个指令的核心技巧,能解决 Angular 开发中 80% 的 DOM 结构控制场景。后续还可以探索自定义结构指令,进一步扩展 Angular 的 DOM 操作能力。


1037

被折叠的 条评论
为什么被折叠?



