首頁  >  文章  >  web前端  >  Angular開發問題記錄:元件資料不能即時更新到視圖上

Angular開發問題記錄:元件資料不能即時更新到視圖上

青灯夜游
青灯夜游轉載
2022-12-08 20:25:152073瀏覽

工作中碰到一個問題:Angular元件資料不能即時更新到視圖上,問題本身比較容易解決,但還是總結記錄一下。

Angular開發問題記錄:元件資料不能即時更新到視圖上

先來說一下起因吧。 【相關教學推薦:《angular教學》】

問題起源



MainComponent:

@Component({
  selector: 'main',
  template: `
    <MenuComponent [isReport]="isReport">
	  </MenuComponent>
 `,
  changeDetection:ChangeDetectionStrategy.OnPush
})
export class MainComponent {
  ...
}
現在有一個MainComponent,我需要在這個元件中引用一個另一個元件MenuComponent
import { Component, Input} from &#39;@angular/core&#39;;
import { Subject, debounceTime } from &#39;rxjs&#39;;

@Component({
  selector: &#39;movie&#39;,
  styles: [&#39;div {border: 1px solid black}&#39;],
  template: `
    <div (mouseover)="mouseOver()">
      <h3>{{ menu }}</h3>
    </div>`
})
export class MovieComponent {
  @Input() isReport: boolean = false;
  menu: string = &#39;我是Menu&#39;;

  mouseOver$: Subject<any> = new Subject();

  ngOnInit(): void {
    this.mouseOver$.pipe(debounceTime(250)).subscribe((data) => {
       this.menu = &#39;New: &#39; + 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 = &#39;New: &#39; + this.menu;
    });
}
###引起問題的地方就是這###debounceTime###,這個之前在介紹Rxjs原理的時候,說過這個是異步的。之前掌握的東西,終於派上用場了。 ###

总结一下,就是mouseover是异步的,会触发变更检测,但是由于debounceTime是异步又嵌套了一下,debounceTime一般是用setTimeout来实现的。所以,debounceTime里的数据变化并不能及时的显示到视图中。终于找到问题的根源了。啦啦啦。问题找到了,那解决起来多easy啊。它不是不会触发变更检测吗,我就手动让它触发一下吧。

import { Component, Input, ChangeDetectorRef } from &#39;@angular/core&#39;;
import { Subject, debounceTime } from &#39;rxjs&#39;;

@Component({
  selector: &#39;movie&#39;,
  styles: [&#39;div {border: 1px solid black}&#39;],
  template: `...`
})
export class MovieComponent {
  ...

  constructor(private cd: ChangeDetectorRef){}

  ngOnInit(): void {
    this.mouseOver$.pipe(debounceTime(250)).subscribe((data) => {
       this.menu = &#39;New: &#39; + this.menu;

       this.cd.detectChanges();
    });
  }

  ...
}

总结


  • 平时多注意知识积累,不能按照网上说的解决方案复制过来就解决了,遇到简单问题这样是没有问题的,遇到复杂的就没办法了;

  • 当设置为Onpush策略时,要更加注意,用OnPush就是要减少变更检测的次数,就不要无论什么情况都detectChanges,或markForCheck,失去了意义,还是要规范使用;

  • 要优雅实现代码,项目中居然还看到把父组件的ChangeDetectorRef作为输入属性传到子组件中,一看就不懂变更检测啊;

更多编程相关知识,请访问:编程教学!!

以上是Angular開發問題記錄:元件資料不能即時更新到視圖上的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除