Angular中*ngFor渲染万级数据时如何正确显示加载器
失效的核心原因
你之前的实现跑不通,本质是踩了两个前端/Angular运行的底层机制坑:
- 浏览器的渲染逻辑优先级低于同步执行的JS代码,当你在同一个同步执行栈里先后做了「开加载器→改列表状态触发1万+节点渲染→关加载器」这三件事时,主线程会被大列表的DOM生成逻辑完全占满,根本挤不出空闲时间把加载器画到屏幕上。等所有逻辑跑完,加载状态已经变回false了,用户自然看不到加载效果。
- Angular的变更检测是批量同步执行的,你修改的组件状态不会立刻更新到视图上,要等当前同步栈所有代码跑完才会统一刷新视图,进一步压缩了加载器的渲染窗口。
可落地的解决办法
1. 最低成本临时方案:拆分同步栈,让加载器先渲染出来
不用改现有*ngFor的逻辑,只要把同步执行的逻辑拆到不同的事件循环周期,给浏览器留出渲染加载器的时间就行,直接改onTitleFilter方法:
onTitleFilter(titleFilter: string) { // 先通知父组件展示加载器 this.startLoading.emit(true); // 把后续的列表状态更新丢到宏任务队列 // 此时主线程会先把当前栈跑完:也就是把加载器渲染到页面上 setTimeout(() => { if (!this.resultFilter) { this.selectedTitleFilter = this.selectedTitleFilter === titleFilter ? '' : titleFilter; } // 等Angular把*ngFor的DOM全量挂载完成后,再关加载器 setTimeout(() => { this.startLoading.emit(false); }, 0); }, 0); }
这个方案改完就能看到效果:加载器会先弹出来,哪怕后续大列表渲染堵主线程,用户也能看到加载提示,不会以为程序卡死了。
2. 推荐的根治方案:从源头降低渲染压力
1万-8万条数据全量生成DOM本身就是性能硬伤,哪怕加载器能正常显示,也会让主线程卡好几秒,体验依然很差,建议搭配两个优化从根上解决:
- 给所有
*ngFor加trackBy函数,避免Angular无意义重建重复DOM。把原来的子项*ngFor写法改成:
组件里加对应的trackBy逻辑,只要数据Id不变,Angular就不会重建对应的DOM节点,能减少60%以上的无效渲染:<li *ngFor="let subFilter of filter.SubFilters; trackBy: trackSubFilter" id="qa-{{subFilter.Name}}" (click)="onFilter(subFilter.Name, subFilter.Id, filter.NameFilter)">trackSubFilter = (index: number, subFilter: any) => subFilter.Id; - 用虚拟滚动替代全量*ngFor:不管总共有8万还是10万条数据,虚拟滚动只渲染当前视口范围内的几十个节点,实际DOM体量和几百条数据的场景完全一样,根本不会出现卡顿,加载器只需要在拉取数据的时候展示就行,不需要等渲染完才关闭。
3. 极端场景兜底方案
如果业务上确实要求必须渲染全量DOM(比如需要一次性全量搜索、没有分页逻辑),可以用分批切片渲染的方式避免堵死主线程:每次从全量数组里切100条数据塞进*ngFor的绑定数据源,每批切片的逻辑放在宏任务里执行,让出主线程处理渲染和用户交互,等所有数据渲染完再关加载器,这种方式能彻底避免页面假死。
内容的提问来源于stack exchange,提问作者oni3619
相关产品推荐
相关产品推荐

