一個angular組件,他的生命周期是這樣的
- update bound properties for all child components/directives
- call
ngOnInit
,OnChanges
andngDoCheck
lifecycle hooks on all child components/directives - update DOM for the current component
- run change detection for a child component
- call
ngAfterViewInit
lifecycle hook for all child components/directives
1,接父節點發來的input參數,並把這時的input參數保存,記為oldInput
2,按ngOnChange,ngOnInit,ngDoCheck的順序調用自己的生命周期函數
3,如果有子組件,將子組件的input參數下傳,並依次調用子組件的ngOnChange,ngOnInit,ngDoCheck參數
4,自身做變化檢測,同時更新自己的dom結構,將此時的dom結構保存,記為olddom
5,子組件進行變化檢測,同時更新子組件的dom結構
6,子組件調用ngAfterViewInit
7,自身調用ngAfterViewInit
8,如果是開發模式,會進行第2輪循環,重復1-7
9,第2輪循環的1,4步驟如果發現oldInput不等於input或者olddom不等於dom,就會報ExpressionChangedAfterItHasBeenCheckedError錯誤
如何避免:
1,不要在ngOnChange,ngOnInit,ngDoCheck里面改變父組件下傳的input參數
2,不要在ngAfterViewInit里改變父組件或自身的dom結構
3,可以用異步的方式做變更
4,在父組件的ngAfterViewInit最后調用this.cd.detectChanges();(不推薦)
為什么要做這個檢測:
是為了保證angular單向數據流的穩定性,數據要保證從父組件流向子組件,如果數據流不是按照從上往下的順序,就會出現互相依賴的關系,容易造成循環依賴,可能讓angular應用出現卡死的狀況。
所以如果在開發過程中發現了ExpressionChangedAfterItHasBeenCheckedError的錯誤,一定要小心排查。
附注:
這篇文章其實是對The Hidden Docs In Angular - ngChina 2019 演講這個視頻前半段的總結。