[Day 29] Banana in a box!在那糖衣的背後

昨天介绍了自制 two-way binding 的方式,今天要接着看在背後 Angular 为我们做了哪些事!

一切都从编译後开始

今天会从以下的范例程序码开始,逐步找出 Angular 为什麽可以做到 two-way binding。

<app-slider-wrapper [(sliderValue)]="slider"></app-slider-wrapper>
<div>
  <label>Slider value: </label><label>{{ slider }}</label>
</div>

↑ Block 1

_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineComponent"]({
  // ... 略
  consts: [[3, "sliderValue", "sliderValueChange"]],
  template: function AppComponent_Template(rf, ctx) {
    if (rf & 1) {
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](
        0,
        "app-slider-wrapper",
        0
      );
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"](
        "sliderValueChange",
        function AppComponent_Template_app_slider_wrapper_sliderValueChange_0_listener(
          $event
        ) {
          return (ctx.slider = $event);
        }
      );
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](1, "div");
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](2, "label");
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtext"](3, "Slider value: ");
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](4, "label");
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtext"](5);
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
    }
    if (rf & 2) {
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵproperty"](
        "sliderValue",
        ctx.slider
      );
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵadvance"](5);
      _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtextInterpolate"](
        ctx.slider
      );
    }
  },
  directives: [
    _slider_wrapper_slider_wrapper_component__WEBPACK_IMPORTED_MODULE_1__[
      "SliderWrapperComponent"
    ],
  ]
});

↑ Block 2:被编译後的程序码片段

Block 2 是 Block 1 程序码被编译後的一小片段。可以发现在 consts 属性内,有一个阵列包含着 sliderValuesliderValueChange,以目前现有的结果来看,我们可以大胆的推测 Angular 在编译时期,会将 [(property)] 翻成 property 以及对应的 propertyChange

而相关的证据如下:

if (bindParts[IDENT_BANANA_BOX_IDX]) {
  this._bindingParser.parsePropertyBinding(
    bindParts[IDENT_BANANA_BOX_IDX],
    value,
    false,
    srcSpan,
    absoluteOffset,
    attr.valueSpan,
    targetMatchableAttrs,
    targetProps
  );
  this._parseAssignmentEvent(
    bindParts[IDENT_BANANA_BOX_IDX],
    value,
    srcSpan,
    attr.valueSpan || srcSpan,
    targetMatchableAttrs,
    boundEvents
  );
}

↑ Block 3:Angular template parser 与 two-way binding 有关的部分(Link

在 Block 3 呈现的程序码可以看出当 Angular parser 发现 template 中有 [( )] 的配对时,他会先呼叫 parsePropertyBinding 的方法来处理 property 本身,接着会呼叫 _praseAssignmentEvent 函式来处理对应的 event:

private _parseAssignmentEvent(
    name: string,
    expression: string,
    sourceSpan: ParseSourceSpan,
    valueSpan: ParseSourceSpan,
    targetMatchableAttrs: string[][],
    targetEvents: ParsedEvent[]
  ) {
    this._bindingParser.parseEvent(
      `${name}Change`,
      `${expression}=$event`,
      sourceSpan,
      valueSpan,
      targetMatchableAttrs,
      targetEvents
    );
  }

↑ Block 4:_parseAssignmentEvent 函式内容(Link

有看到那个熟悉的 Change 字样吗?这边就是 Angular 在编译时期将 two-way binding 从 banana in a box 的语法糖,转换成 property 以及 propertyChange 的起始点,後续再经由 compiler 的努力,就可以看到在 Block 2 中的 consts 的内容罗!

小小总结

今天先解开 [( )] Banana in a box 的语法糖为什麽在 Angular 产生的 main.js 内会不见踪影,以及为什麽只需要符合前一篇文章所述的两个条件後,就能够做到 two-way binding 的原因。

至於 Angular 是怎麽处理 event 与 property binding 的部分,就是明天的内容罗!

明天最後一天啦 ?

以下按照入团顺序列出我们团队夥伴的系列文章!

请自由参阅 ?


<<:  菜鸟网页基础DAY29

>>:  今年我想陪着 30 天之 29

【课程推荐】2021/1/23~1/24 软件需求与系统分析实务班

课程对象 软件专案开发商之业务人员与Pre-Sales、软件系统专案经理、软件系统系统分析师。 详细...

Day4 用python写UI-聊聊视窗控件配置管理员-grid方法

今天要介绍的是grid()方法,这个方法就像表格式排版,控件是依据指定的索引位置, 放入表格中,很适...

[Day 29]TensorFlow矩阵运算

今天笔者想聊聊TensorFlow的矩阵运算模式,笔者向来的数学都不是太好,此次30天的尾声想要与大...

GitHub Branch 策略 - 哪一种方式适合你?

若您对於 Git 相当熟悉,你应该对於建立分支(Branch) 应该不陌生。依据 GitHub 官方...

Day11-Go映射map

前言 前面我们介绍了几种变数容器,例如阵列和切片,这些皆以数字做为索引,而今天要介绍的映射(map)...