工作中碰到一個問題:Angular元件資料不能即時更新到視圖上,問題本身比較容易解決,但還是總結記錄一下。
先來說一下起因吧。 【相關教學推薦:《angular教學》】
MainComponent:@Component({
selector: 'main',
template: `
<MenuComponent [isReport]="isReport">
</MenuComponent>
`,
changeDetection:ChangeDetectionStrategy.OnPush
})
export class MainComponent {
...
}
現在有一個MainComponent,我需要在這個元件中引用一個另一個元件MenuComponent。 import { Component, Input} from '@angular/core';
import { Subject, debounceTime } from 'rxjs';
@Component({
selector: 'movie',
styles: ['div {border: 1px solid black}'],
template: `
<div (mouseover)="mouseOver()">
<h3>{{ menu }}</h3>
</div>`
})
export class MovieComponent {
@Input() isReport: boolean = false;
menu: string = '我是Menu';
mouseOver$: Subject<any> = new Subject();
ngOnInit(): void {
this.mouseOver$.pipe(debounceTime(250)).subscribe((data) => {
this.menu = 'New: ' + this.menu;
});
}
mouseOver(): void {
this.mouseOver$.next(this.menu);
}
}
這個MenuComponent在其他的頁面使用起來是正常的,而且因為是Menu元件,所以上面有多
事件,這些事件也可以正常運作。但,這個MenuComponent 放在
MainComponent中,mouseover事件就有問題了,調試了下
mouseover事件,程式碼都正確執行了,感覺程式碼並沒有什麼問題。因為這個元件放在其他頁面裡,行為完全正常,所以感覺不是元件本身的問題。 表現的現像是:
Menu裡的mouseover行為很怪異,你over到A的時候,顯示的是B的數據,當你over到B的時候顯示的是A的數據,整個錯亂了。 第一個反應就是,這會不會是和MainComponent中的
mouseover事件衝突了呢? 檢查了一遍,沒有發現問題所在。但有意外收穫,啊啊啊,MainComponent元件使用的是
OnPush變更偵測策略,難怪其他頁面都好使,就這個地方有問題了。好了,問題應該就是由OnPush造成的。關於變更偵測策略的,那還不是手到擒來,在熟悉不過了,來來來,一起簡單看一下這個OnPush。
OnPush策略
Angular有兩種變更偵測的策略,一種是Default,另一種就是這個OnPush。 OnPush這個變更偵測策略主要為了改善效能。當我們設定組件裝飾器的 changeDetection為OnPush的時候,Angular 每次觸發變更檢測後會跳過該組件和該組件的所以子組件變化檢測。 好了,我們也知道什麼是OnPush變更偵測策略了,它會跳過目前元件和其子元件的變更 偵測。也就是說,你改變這個元件的屬性值,但這些屬性值不會更新到視圖上,也就是元件陣列和視圖不一致。那我們知道了這一點,再回去看一下
MenuComponent。 由於
MainComponent的變更策略設定為了OnPush,他的子元件的變更偵測策略會跳過,也就是
MenuComponent大部分人可能花一分鐘了解了OnPush是什麼,但沒有了解透徹。繼續往下看。 在OnPush 策略下,以下這種情況會觸發元件的變更偵測:目前元件或子元件之一觸發了事件如果OnPush元件或其子元件之一觸發(DOM/BOM)事件,例如
click,mouseover,
mouseleave,
keydown要注意的是在
OnPush
Promise.resolve().then()this.http.get('. ..').subscribe()
原來如此,儘管是
OnPush###策略,但是###DOM/BOM###事件還是會觸發變更偵測的,所以###MenuComponent###的視圖還是會有變化的,也就是這個變更偵測是起作用的。但問題還是沒有解決,Menu mouseover的時候還是會錯亂!再來看一下程式碼。 ###ngOnInit(): void { this.mouseOver$.pipe(debounceTime(250)).subscribe((data) => { this.menu = 'New: ' + this.menu; }); }###引起問題的地方就是這###debounceTime###,這個之前在介紹Rxjs原理的時候,說過這個是異步的。之前掌握的東西,終於派上用場了。 ###
总结一下,就是mouseover是异步的,会触发变更检测,但是由于debounceTime是异步又嵌套了一下,debounceTime一般是用setTimeout来实现的。所以,debounceTime里的数据变化并不能及时的显示到视图中。终于找到问题的根源了。啦啦啦。问题找到了,那解决起来多easy啊。它不是不会触发变更检测吗,我就手动让它触发一下吧。
import { Component, Input, ChangeDetectorRef } from '@angular/core'; import { Subject, debounceTime } from 'rxjs'; @Component({ selector: 'movie', styles: ['div {border: 1px solid black}'], template: `...` }) export class MovieComponent { ... constructor(private cd: ChangeDetectorRef){} ngOnInit(): void { this.mouseOver$.pipe(debounceTime(250)).subscribe((data) => { this.menu = 'New: ' + this.menu; this.cd.detectChanges(); }); } ... }
平时多注意知识积累,不能按照网上说的解决方案复制过来就解决了,遇到简单问题这样是没有问题的,遇到复杂的就没办法了;
当设置为Onpush策略时,要更加注意,用OnPush就是要减少变更检测的次数,就不要无论什么情况都detectChanges,或markForCheck,失去了意义,还是要规范使用;
要优雅实现代码,项目中居然还看到把父组件的ChangeDetectorRef作为输入属性传到子组件中,一看就不懂变更检测啊;
更多编程相关知识,请访问:编程教学!!
以上是Angular開發問題記錄:元件資料不能即時更新到視圖上的詳細內容。更多資訊請關注PHP中文網其他相關文章!