Angular 12中*ngFor内使用*ngIf调用权限校验方法频繁触发问题及优化方案咨询
Angular 12中IsAllowed方法被频繁调用的原因及优化方案
一、为什么会出现数千次调用?
这是Angular的变更检测机制导致的典型问题:
- 当你在模板的
*ngIf里直接调用方法(比如IsAllowed(['admin','user'])),Angular在每一次变更检测周期都会执行这个方法。而Angular的变更检测触发场景非常多——用户点击操作、输入框输入、HTTP请求完成、浏览器窗口resize、定时器触发等等,都会触发一轮检测。 - 另外你每次调用时都传入了新的数组字面量
['admin','user'],这会让Angular误以为输入值发生了变化,进一步增加方法的调用次数。哪怕只有8个menuItems,多次变更检测叠加起来,就会出现数千次的console输出。
顺便提个小细节:你的IsAllowed方法如果没有匹配到角色,没有返回值(默认返回undefined),在*ngIf里会被视为false,建议补上return false的逻辑,避免潜在的奇怪问题:
IsAllowed(allowedRoles: string[]): boolean { console.log("test"); for (const role of userRoles) { if (allowedRoles.includes(role)) { return true; } } // 补上默认返回,逻辑更严谨 return false; }
二、更优的实现方案(适配动态allowedRoles)
因为每个菜单项的权限参数是动态的,推荐下面两种方案:
方案1:提前计算权限并缓存结果(最推荐)
在组件初始化或者menuItems加载完成后,给每个menuItem添加一个hasAccess属性,只计算一次权限结果,模板直接使用这个缓存值:
// 组件代码 ngOnInit(): void { // 假设你的menuItems已经加载完成,且每个item有自己的allowedRoles字段 this.menuItems = this.menuItems.map(item => ({ ...item, hasAccess: this.checkAccess(item.allowedRoles) })); } // 把权限检查改成内部私有方法,不需要暴露给模板 private checkAccess(allowedRoles: string[]): boolean { for (const role of userRoles) { if (allowedRoles.includes(role)) { return true; } } return false; }
模板里直接用缓存的属性,彻底避免变更检测时重复调用:
<ul id="respMenu" class="horizontal-menu"> <ng-container *ngFor="let menuItem of menuItems; let i = index"> <li *ngIf="menuItem.hasAccess"> <a> <span class="title">{{menuItem.title}}</span> </a> </li> </ng-container> </ul>
如果menuItems或者用户权限会动态更新,只需要在数据变化后重新执行一次map逻辑更新hasAccess即可。
方案2:使用纯管道(Pipe)缓存计算结果
Angular的纯管道会自动缓存输入参数对应的结果,只有当输入参数的引用发生变化时,才会重新执行转换逻辑,非常适合这种需要重复计算的场景。
首先创建一个权限检查管道:
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'checkAccess' }) export class CheckAccessPipe implements PipeTransform { // 把用户权限作为参数传入,也可以通过注入权限服务获取 transform(allowedRoles: string[], userRoles: string[]): boolean { for (const role of userRoles) { if (allowedRoles.includes(role)) { return true; } } return false; } }
然后在模板中使用这个管道:
<ul id="respMenu" class="horizontal-menu"> <ng-container *ngFor="let menuItem of menuItems; let i = index"> <li *ngIf="menuItem.allowedRoles | checkAccess: userRoles"> <a> <span class="title">{{menuItem.title}}</span> </a> </li> </ng-container> </ul>
这种方案适合menuItems或者用户权限频繁动态变化的场景,管道会自动处理缓存和重新计算逻辑。
内容的提问来源于stack exchange,提问作者Faisal
相关产品推荐
相关产品推荐

