【Angular】關於AsyncPipe你不知道的3件事!

來源:互聯網
上載者:User

我的angular2項目:
http://git.oschina.net/zt_zhong/CodeBe
原文地址:https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html

你肯定聽說過Angular的AsyncPipe對不對。它是一個非常方便的管道(Pipe),可以讓我們在模版中使用,所以我們不需要去處理從Observables或者Promises中的資料。 結果就是,AsyncPipe有一些神奇的功能,可能不是那麼明顯。在這篇文章中,我們將會對這個有用的小工具的內部工作原理做一些闡述。 訂閱到Observables

通常我們考慮使用AsyncPipe時,只是用它來解析從http調用中返回的資料。我們發出一個http調用,返回一個Observable對象,然後做一些轉換(比如:map(…).filter(…)),最後將一個Observable暴露給我們組件的模板。這裡是就是我們通常使用的時候的樣子:

...@Component({  ...  template: `    <md-list>      <a md-list-item        *ngFor="let contact of contacts | async"        title="View {{contact.name}} details">        <img md-list-avatar [src]="contact.image" alt="Picture of {{contact.name}}">        <h3 md-line>{{contact.name}}</h3>      </a>    </md-list>`,})export class ContactsListComponent implements OnInit {  contacts: Observable<Array<Contact>>;  constructor(private contactsService: ContactsService) {}  ngOnInit () {    this.contacts = this.contactsService.getContacts();  }}

在這種情況下,我們的Observable 就是我們所說的短命(原文short-lived)的。Observable 在這種情況下只發射一個值,就是一個contacts數組,並在發射之後結束。這是使用http的典型情況,它基本上是使用Promises的唯一情境。

然而,我們可以完全擁有發出多個值的Observables。想想使用websockets的例子。我們可能有一個隨著時間的推移而構建的數組。我們來類比一個可以發出數字數組的Observable。但是,不是一次只發出一個數組,它會在每次添加新項目時發出一個數組。為了不讓數組無限增長,我們將其限制為最後五個項目。

...@Component({  selector: 'my-app',  template: `    <ul>      <li *ngFor="let item of items | async">{{item}}</li>    </ul>`})export class AppComponent {  items = Observable.interval(100)                    .scan((acc, cur)=>[cur, ...acc].slice(0, 5), []);             }

注意我們的列表如何保持良好的同步,不用多說,感謝AsyncPipe。 跟蹤引用

如果沒有AsyncPipe的協助,讓我們回到上面的代碼並重構上面的代碼。但是,在這裡我們使用一個按鈕來重建數字,並且在每次重建這個序列的時候隨機播放元素的背景色。

...@Component({  selector: 'my-app',  template: `    <button (click)="newSeq()">New random sequence</button>    <ul>      <li [style.background-color]="item.color"          *ngFor="let item of items">{{item.num}}</li>    </ul>`})export class AppComponent {  items = [];  constructor () {    this.newSeq();  }  newSeq() {    // generate a random color    let color = '#' + Math.random().toString(16).slice(-6);    Observable.interval(1000)          .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), [])          .subscribe(items => this.items = items);  }}


線上運行地址

運行這個例子,注意到什麼了嗎。隨著按鈕的每次點擊,顏色會在越來越多的不同顏色之間來復原動。那就是因為在這種情況下,Observable就是我們所說的長壽(原文:long-lived)的。
此外,每次我們點擊按鈕,我們都在建立另外一個這些長壽的Observable,而沒有清理以前的。

讓我們重構代碼來跟蹤訂閱,並在每次建立一個新的Observable時,都會刪除我們長壽的Observable。

...export class AppComponent {  ...  subscription: Subscription;  newSeq() {    if (this.subscription) {      this.subscription.unsubscribe();    }    // generate a random color    let color = '#' + Math.random().toString(16).slice(-6);    this.subscription = Observable.interval(1000)          .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), [])          .subscribe(items => this.items = items);  }}

線上運行地址
每次我們訂閱一個Observable ,我們將它儲存到組件執行個體的一個屬性中。然後,當我們再次運行newSeq的時候,我們檢查this.subscription是否存在,如果存在的話,我們就需要去調用unsubscribe來取消訂閱。這就是為什麼我們看不到我們的列表在各種顏色之間跳轉,不管我們點擊了按鈕多少次。

現在我們再次看下AsyncPipe。讓我們來改變ngFor來應用AsyncPipe。

@Component({  selector: 'my-app',  template: `    <button (click)="newSeq()">New random sequence</button>    <ul>      <li [style.background-color]="item.color"          *ngFor="let item of items | async">{{item.num}}</li>    </ul>`})export class AppComponent {  items: Observable<any>;  constructor () {    this.newSeq();  }  newSeq() {    // generate a random color    let color = '#' + Math.random().toString(16).slice(-6);    this.items = Observable.interval(1000)                           .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), []);  }}

線上運行地址
我確信你已經聽說AsyncPipe一旦組件被銷毀,就會從Observables取消訂閱。但是,一旦運算式的引用發生變化,你也知道它取消訂閱嗎。沒錯,只要我們為此分配一個新的Observable,則AsyncPipe將自動取消訂閱先前綁定的Observable。這不僅使我們的代碼變得美觀乾淨,還能保護我們免受非常微妙的記憶體流失。 標記要檢查的東西

好的。 我們為您提供最後一個漂亮的AsyncPipe功能。如果你已經閱讀了我們的關於Angular變更檢測的文章,你應該知道,你可以使用OnPush策略來進一步加快變更檢測過程。我們重構我們的例子,並引入一個SeqComponent來顯示序列,而我們的根組件將管理資料並通過輸入綁定傳遞它。

我們開始建立一個非常簡單的SeqComponent。

@Component({  selector: 'my-seq',  template: `    <ul>      <li [style.background-color]="item.color"           *ngFor="let item of items">{{item.num}}</li>    </ul>`})export class SeqComponent {  @Input()  items: Array<any>;}

請注意@Input()裝飾器items屬性,這意味著組件將通過屬性綁定從外部接收。
我們的根組件維護一個數組seqs,並通過點擊一個按鈕將新的長壽的Observables推入它。
它使用* ngFor將這些Observables中的每一個傳遞給一個新的SeqComponent執行個體。還要注意,我們在我們的屬性綁定運算式([items] =“seq | async”)中使用AsyncPipe來傳遞純數組而不是Observable,因為這是SeqComponent所期望的。

@Component({  selector: 'my-app',  template: `    <button (click)="newSeq()">New random sequence</button>    <ul>      <my-seq *ngFor="let seq of seqs" [items]="seq | async"></my-seq>    </ul>`})export class AppComponent {  seqs = [];  constructor () {    this.newSeq();  }  newSeq() {    // generate a random color    let color = '#' + Math.random().toString(16).slice(-6);    this.seqs.push(Observable.interval(1000)                           .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), []));  }}

到目前為止,我們還沒有對潛在的變更檢測策略做出任何改變。 如果您點擊按鈕幾次,請注意我們如何擷取多個列表,以不同的時間獨立更新。
線上運行地址
然而,在變更檢測方面,這意味著每次Observables 都可以檢查所有組件。這是浪費資源。通過將SeqComponent的更改檢測設定為OnPush,我們可以做得更好,這意味著如果輸入(我們的案例中的數組)發生變化,它將只檢查它的綁定。

@Component({  changeDetection: ChangeDetectionStrategy.OnPush,  selector: 'my-seq',  ...})

這非常有效。但是這裡有個問題:它只在這種情況下起作用,因為我們的Observable 每次都是建立一個新的數組和值。即使這實際上並不太糟糕,實際上在大多數情況下都是有益的,讓我們考慮一下我們使用一個不同的實現來修改現有的數組,而不是每次重新建立它。

Observable.interval(1000)          .scan((acc, num)=>{            acc.splice(0, 0, {num, color});            if (acc.length > 5) {              acc.pop()            }            return acc;          }, [])

如果我們嘗試,OnPush似乎不再工作,因為項目的引用根本不會改變。 實際上,當我們嘗試這樣做時,我們看到每個列表都不會超出其第一個元素。
線上運行地址

再次認識AsyncPipe。 我們來改變我們的SeqComponent,所以它需要一個Observable而不是一個數組作為它的輸入。

@Component({  changeDetection: ChangeDetectionStrategy.OnPush,  selector: 'my-seq',  template: `    <ul>      <li [style.background-color]="item.color"           *ngFor="let item of items | async">{{item.num}}</li>    </ul>`})export class SeqComponent {  @Input()  items: Observable<Array<any>>;}

另請注意,它現在將AsyncPipe應用於其模板,因為它不再處理一個簡單的數組。 我們的AppComponent還需要更改,以便不再在屬性綁定中應用AsyncPipe。

<ul>  <my-seq *ngFor="let seq of seqs" [items]="seq"></my-seq></ul>

線上運行地址

cool。現在似乎已經開始有效了。

讓我們回顧一下,我們的數組執行個體不會改變,我們的Observable的執行個體也不會改變。那麼為什麼OnPush在這種情況下起作用了。 原因可以在AsyncPipe本身的原始碼中找到

private _updateLatestValue(async: any, value: Object): void {  if (async === this._obj) {    this._latestValue = value;    this._ref.markForCheck();  }}

AsyncPipe標記要檢查的組件的ChangeDetectorRef,有效地告訴變更檢測,該組件可能會發生更改。如果您想更詳細地瞭解我們如何工作,我們建議您閱讀我們深入的變更檢測文章。 總結

我們使用AsyncPipe作為一個漂亮的小工具來在我們的組件中儲存幾行代碼。 實際上,它隱藏了來自我們的管理非同步任務的很多複雜性。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.