1. Angular 错误:ExpressionChangedAfterItHasBeenCheckedError
最近在做项目的时候发现一个错误:ExpressionChangedAfterItHasBeenCheckedError。
经过Google后了解到,这个是只有dev环境下才会报的错误,跟变更检测有关。
大意是:angular 在render页面时某个属性已赋值,然而在render结束后 AfterViewChecked 的时候属性值却发生了变化,这可能导致给用户显示的信息是错误的,所以爆出该错误。
1.1. 情景再现:
template:
父组件 aComponent 内循环输出子组件 bComponent。
<div *ngfor(let item of list; index of i)>
<bComponent [img]="imgData[i]"></bComponent>
</div>
aComponent.ts
ngAfterViewInit() {
异步获取list->then(()=>{
获取imgData[];
)
}
bComponent.ts
@Input() imgData;
images: string[] = [];
ngAfterViewInit() {
this.images = imgData;
}
在我加了一些log后发现执行顺序:
aComponent ngOnInit()
aComponent ngAfterViewInit()
bComponent ngOnInit()
bComponent ngAfterViewInit()
因为刚开始异步获取list还没得到结果,所以父组件 ngOnInit-> ngAfterViewInit;
等到获得到list, 页面才会循环渲染输出子组件,执行其生命周期。
因为我将 this.images = imgData; 操作放在了ngAfterViewInit()生命周期里,
根据组件变更检测的执行顺序(digest cycle):
更新所有子组件/指令的绑定属性
调用所有子组件/指令的三个生命周期钩子:ngOnInit,OnChanges,ngDoCheck
更新当前组件的 DOM
为子组件执行变更检测(译者注:在子组件上重复上面三个步骤,依次递归下去)
为所有子组件/指令调用当前组件的 ngAfterViewInit 生命周期钩子
当更新 DOM 时,images还是空[],然后在 ngAfterViewInit() 被修改。
如果处于开发者模式,Angular 还会执行上面的 digest cycle 循环核查。
angular 在第二次 digest cycle 的时候发现DOM值和输入属性值不同,于是就报错了。
1.2. 解决:
将 this.images = imgData; 赋值操作 放到 ngOnInit() 内即可。
总结:了解组件的生命周期和执行过程还是很重要的,一不小心就可能掉坑里。
1.3. 参考:
[译] 关于 ExpressionChangedAfterItHasBeenCheckedError 错误你所需要知道的事情
评论
发表评论