[Day 27] 微探讨 Pure pipe 与 Impure pipe

今天要介绍的 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 28] 复刻 Rails - Routing 威力加强版 - 2

>>:  Day 27 - 使用 CDK 创建 CloudWatch 也能发送 Alarm 到 LINE 的系统

Day 05-选择React & Redux

!前提小补充! UI: User Interface(使用者介面),设计页面,须注意到网页页面使用的...

Class and Style Bindings

透过昨天的范例我们知道要绑定HTML属性需要使用v-bind指令,而今天我们要介绍的是v-bind绑...

Day 9 ( 中级 ) 空中传爱 ( 广播 )

空中传爱 ( 广播 ) 教学原文参考:空中传爱 ( 广播 ) 这篇文章会介绍如何使用「发送广播」、「...

【13】模型套不套用资料增强 (Data Augmentation) 的比较实验

Colab连结 资料增强(Data Augmentation),是一个当今天资料集样本不多时,透过调...

[Day19 ] Prototype Pollution - Prototype污染

前言 你使用过Prototype,那你知道它可以被污染吗? 正文 概念 Javascript的物件透...