今天要介紹的 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
結果:
↑ Image 1
昨天的文章有稍微提到 ɵɵ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 方法內塞了很多的邏輯運算,當傳入的資料量越大,就會更顯著的拖慢應用程式的效能。
以下按照入團順序列出我們團隊夥伴的系列文章!
請自由參閱 ?