iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 27
0
Modern Web

從巨人的 Tip 看 Angular系列 第 27

[Day 27] 微探討 Pure pipe 與 Impure pipe

  • 分享至 

  • xImage
  •  

今天要介紹的 Tip 是有關於 pipe 的 pure 與 impure,當沒有任何額外的設定下,自行建立的 pipe 都會是屬於 pure pipe,這一點我們可以從原始碼的說明得知:

export interface Pipe {
  /**
   * The pipe name to use in template bindings.
   * Typically uses [lowerCamelCase](guide/glossary#case-types)
   * because the name cannot contain hyphens.
   */
  name: string;

  /**
   * When true, the pipe is pure, meaning that the
   * `transform()` method is invoked only when its input arguments
   * change. Pipes are pure by default.
   *
   * If the pipe has internal state (that is, the result
   * depends on state other than its arguments), set `pure` to false.
   * In this case, the pipe is invoked on each change-detection cycle,
   * even if the arguments have not changed.
   */
  pure?: boolean;
}

↑ Block 1:Pipe 的 interface

因為 pipe 密切的與 HTML template 合作,所以每一次的 change detect 都有可能會需要重新使用 pipe 來處理資料,並轉成指定的格式輸出。

而 pure pipe 的特性就是,它只有當傳入的值是 string、number、boolean 等原生型別時,Angular 才會執行 pure pipe 的 transform 方法。如果傳入的值是一個 Array 或是 Object,除非這兩者的 reference 有被變更,否則並不會觸發 pure pipe 的 transform 方法。

impure pipe 的特性與 pure pipe 剛好相反,每當有 change detection 發生時,Angular 就會執行 impure pipe 的 transform 方法。建立 impure pipe 的方法也非常簡單,只需要將 Pipe decorator 的 pure 屬性設為 false 就可以了。

兩者間的差別可以用下方的簡單範例來證明:

@Pipe({
  name: 'filter',
  pure: true
})
export class FilterPipe implements PipeTransform {

  transform(value: Array<Obj>): Array<Obj> {
    return value.filter((obj: Obj) => obj.show);
  }
}

↑ Block 2:Pure pipe

@Pipe({
  name: 'impureFilter',
  pure: false
})
export class ImpureFilterPipe implements PipeTransform {

  transform(value: Array<Obj>): Array<Obj> {
    return value.filter((obj: Obj) => obj.show);
  }
}

↑ Block 3:Impure pipe

<label>Name:</label><input #a>
<label>title:</label><input #b>
<label>show:</label><input #c>
<button (click)="append(a.value, b.value, c.value)">Append</button>
<div>
  <p>Impure:</p>
  <ng-container>
    <p *ngFor="let item of (objs | impureFilter)">{{ item | json }}</p>
  </ng-container>
  <p>Pure:</p>
  <ng-container>
    <p *ngFor="let item of (objs | filter)">{{ item | json }}</p>
  </ng-container>
</div>

↑ Block 4:HTML template

結果:

01.gif

↑ Image 1

Pure 與 impure 之間在 Angular 原始碼內的差異

昨天的文章有稍微提到 ɵɵpipeBind1 這個 instruction,兩種不同 pipe 的差異其實也就在這個 instruction 裡:

isPure(lView, index) ?
  pureFunction1Internal(
      lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance):
  pipeInstance.transform(v1));

↑ Block 5

impure pipe 的處理相對簡單,因為對於 Angular 來說,這類型的 Pipe 在每次的 change detection 就是都去執行 transform 方法就好,完全不做其他比對的處理。

而 pure pipe 的行為就稍微複雜一些,Angular 會另外呼叫一個 pureFunction1Internal 函式:

export function pureFunction1Internal(
    lView: LView, bindingRoot: number, slotOffset: number, pureFn: (v: any) => any, exp: any,
    thisArg?: any): any {
  const bindingIndex = bindingRoot + slotOffset;
  return bindingUpdated(lView, bindingIndex, exp) ?
      updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
      getPureFunctionReturnValue(lView, bindingIndex + 1);
}

↑ Block 6:pureFunction1Internal 函式

Block 6 的 pureFunction1Internal 函式內還呼叫了 bindingUpdate 來協助判斷傳入的值跟上一次保留的值有沒有不同,有更新的話才會呼叫 pureFn,並使用 updateBinding 函式來更新 lView 內的資料,如果沒有更新的話,就會直接透過 getPureFunctionReturnValue 這個函式來取得上次保留的值。


以上就是關於 pure pipe 與 impure pipe 的介紹!

雖然從原始碼的角度來看,impure pipe 反而做了比較少的處理就直接呼叫 transform 方法,但實際對於效能而言,會因為 transform 方法的實作內容而有不同程度的影響,若在 impure pipe 的 transform 方法內塞了很多的邏輯運算,當傳入的資料量越大,就會更顯著的拖慢應用程式的效能。

最後一週啦 ?

以下按照入團順序列出我們團隊夥伴的系列文章!

請自由參閱 ?


上一篇
[Day 26] 從 AsyncPipe 出發,微探討 Angular 處理 pipe 的流程
下一篇
[Day 28] banana in a box!關於雙向繫結功能的語法糖
系列文
從巨人的 Tip 看 Angular30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言