前端(vue)入門到精通課程,老師線上輔導:聯絡老師
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:
先來說一下起因吧。【相關教學推薦:《》】
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元件,所以上面很有多mouseover事件,這些事件也可以正常工作。但,這個 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變更檢測不起作用了。但是,你會發現當你操作Menu的時候檢視還是會有變化的。這是怎麼回事?
大部分人可能花一分鐘瞭解了OnPush是什麼,但是沒有了解透徹。繼續往下看。
在 OnPush 策略下,以下這種情況會觸發元件的變化檢測:
如果OnPush元件或其子元件之一觸發(DOM/BOM)事件,例如 click,mouseover,mouseleave,resize, keydown,則將觸發變化檢測(針對元件樹中的所有元件)。
需要注意的是在OnPush策略中,以下操作不會觸發變化檢測:
setTimeout()
setInterval()
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開發問題記錄:元件資料不能實時更新到檢視上的詳細內容,更多請關注TW511.COM其它相關文章!