刚接手一个老项目,首页加载慢得像在等泡面煮熟。打开 DevTools 一看,DOM 节点数破 2000,其中一大半是“明明看不见却还挂着”的元素——比如一个用户没登录时也渲染出来的个人中心弹窗,只是加了 [hidden]="!isLoggedIn"。
hidden 是“藏起来”,不是“删掉”
hidden 属性本质就是 CSS 的 display: none。元素还在 DOM 里,绑定还在跑,事件监听器照常注册,子组件照样初始化、触发 ngOnInit、执行 ngOnChanges,甚至可能悄悄发请求。
举个例子:
<user-profile *ngIf="isLoggedIn"></user-profile>
<!-- vs -->
<user-profile [hidden]="!isLoggedIn"></user-profile>
前者:用户没登录时,user-profile 组件压根不创建,不走任何生命周期,内存里没它;
后者:不管登没登录,组件都创建了,模板渲染了,输入输出属性绑定了,change detection 还得每轮都扫它一遍。
ngIf 是“按需上岗”,有开销但更干净
*ngIf 会彻底销毁/重建视图。首次显示时多一次创建开销(实例化、渲染、绑定),但隐藏后内存释放、检测树变轻。适合切换频率低、内容较重的模块,比如后台管理页的「系统日志」Tab,平时根本不用,何必一直占着内存?
不过要注意:频繁切换(比如鼠标 hover 触发的下拉菜单)用 *ngIf 可能卡顿,因为反复创建销毁成本高。这时候 [hidden] 或 [style.display] 更合适——毕竟藏起来比重新招人再培训快。
实测小对比(Chrome Performance 面板)
在含 10 个 async 管道 + 表单控件的组件上:
- 用
[hidden]切换 10 次:change detection 平均耗时 8.2ms/轮,内存占用稳定在 42MB; - 用
*ngIf切换 10 次:首次显示 15ms,后续平均 3.1ms/轮,内存峰值回落到 36MB;
差别不大?那试试把组件换成带 ngModel 和 Validators.required 的 50 行表单——[hidden] 下 change detection 直接涨到 14ms,而 *ngIf 隐藏后这部分完全归零。
一句话选法
要彻底省资源、内容重、切换少 → 用 *ngIf;
要快速显隐、内容轻、切换勤(如 Tooltip、折叠面板)→ 用 [hidden] 或 [class.hidden]。
别再所有“暂时不用”都塞 [hidden] 了,有时候藏得越深,拖得越累。