Angular中简单函数多次调用的原因及单次调用解决方案
为什么Angular模板里的
myFunc()会被调用4次? 嘿,我明白你为啥困惑了——明明只有一个item,函数却被调用了4次,这事儿其实是Angular的变更检测机制在背后搞鬼,我来给你拆解清楚:
原因分析
Angular会在多个时机触发变更检测,每次检测时,模板里的所有表达式(包括你写的{{myFunc(item?.id) ? "1" : "2"}})都会被重新执行一遍。在你的例子里,组件启动过程中Angular一共跑了4次变更检测周期:
- 组件初始化时的首次渲染检测
- Angular内部的初始化后校验检测
- 微任务队列完成后的额外检测
- 视图更新后的确认检测
这就导致了myFunc被调用了4次。而且要注意:哪怕你的数据完全没变化,只要变更检测跑起来,模板里的函数就会被执行,这也是为什么不推荐在模板里直接调用函数的原因——会带来不必要的性能开销。
正确实现:让函数只调用一次
有两种常用的方案可以解决这个问题,都能避免模板里的重复调用:
1. 提前在组件中计算好结果,模板直接用属性
在组件初始化或者数据更新时,把计算好的值存到items的对象里,模板直接读取这个预计算的属性就行:
app.component.ts:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { items = [{"id": 1}]; ngOnInit() { // 初始化时一次性计算好显示值 this.items.forEach(item => { item.displayText = this.myFunc(item.id) ? "1" : "2"; }); } myFunc(itemId) { console.log("i was called"); return true; } }
app.component.html:
<table> <thead> <tr> <th>Header 1</th> </tr> </thead> <tbody> <tr *ngFor="let item of items"> <td> <a>{{item.displayText}}</a> </td> </tr> </tbody> </table>
这样myFunc只会在ngOnInit里被调用一次,完全符合你的预期。
2. 使用Angular纯管道(推荐复用场景)
如果你的计算逻辑需要在多个组件里复用,或者依赖动态变化的输入值,可以自定义一个纯管道。纯管道只有当输入值发生变化时才会重新计算,初始只会执行一次:
第一步:创建自定义管道
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'displayText' }) export class DisplayTextPipe implements PipeTransform { transform(itemId: number): string { console.log("pipe was called"); // 这里替换成你的myFunc逻辑 return itemId ? "1" : "2"; } }
第二步:在组件中声明管道,模板使用
<table> <thead> <tr> <th>Header 1</th> </tr> </thead> <tbody> <tr *ngFor="let item of items"> <td> <a>{{item?.id | displayText}}</a> </td> </tr> </tbody> </table>
这样初始只会调用一次管道的transform方法,只有当item.id变化时才会再次执行。
小提示
永远记住:模板表达式应该是无副作用、幂等的,直接调用函数很容易带来意外的多次执行和性能问题,优先用预计算属性或者管道来替代模板里的函数调用。
内容的提问来源于stack exchange,提问作者Warren Crasta
相关产品推荐
相关产品推荐

