Component HTML:

   <input type="text" maxlength="40" nz-input placeholder="Input Book Name to search" [(ngModel)]="bookName" (keyup)="search(bookName)"/>

这个bookName是Component的一个属性:

运行时渲染到包含了[(ngModel)]的模板:

创建directive实例:


创建ngModel实例:

我在UI输入一个a,因为双向绑定,Component的bookName值也会变成a,这是怎么实现的呢?

原来每当UI发生变化,触发refreshView重绘界面,会执行checkhooks:

/*** Executing hooks requires complex logic as we need to deal with 2 constraints.** 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only* once, across many change detection cycles. This must be true even if some hooks throw, or if* some recursively trigger a change detection cycle.* To solve that, it is required to track the state of the execution of these init hooks.* This is done by storing and maintaining flags in the view: the {@link InitPhaseState},* and the index within that phase. They can be seen as a cursor in the following structure:* [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]* They are are stored as flags in LView[FLAGS].** 2. Pre-order hooks can be executed in batches, because of the select instruction.* To be able to pause and resume their execution, we also need some state about the hook's array* that is being processed:* - the index of the next hook to be executed* - the number of init hooks already found in the processed part of the  array* They are are stored as flags in LView[PREORDER_HOOK_FLAGS].*/
/*** Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were* executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read* / write of the init-hooks related flags.* @param {?} lView The LView where hooks are defined* @param {?} hooks Hooks to be run* @param {?=} nodeIndex 3 cases depending on the value:* - undefined: all hooks from the array should be executed (post-order case)* - null: execute hooks only from the saved index until the end of the array (pre-order case, when* flushing the remaining hooks)* - number: execute hooks only from the saved index until that node index exclusive (pre-order* case, when executing select(number))* @return {?}*/
function executeCheckHooks(lView, hooks, nodeIndex) {callHooks(lView, hooks, 3 /* InitPhaseCompleted */, nodeIndex);
}

函数wrapOnChangesHook_inPreviousChangesStorage:

function ɵɵNgOnChangesFeature(definition) {if (definition.type.prototype.ngOnChanges) {definition.setInput = ngOnChangesSetInput;((/** @type {?} */ (definition))).onChanges = wrapOnChanges();}
}
// This option ensures that the ngOnChanges lifecycle hook will be inherited
// from superclasses (in InheritDefinitionFeature).
/** @nocollapse */
// tslint:disable-next-line:no-toplevel-property-access
((/** @type {?} */ (ɵɵNgOnChangesFeature))).ngInherit = true;
/*** @return {?}*/
function wrapOnChanges() {return (/*** @this {?}* @return {?}*/function wrapOnChangesHook_inPreviousChangesStorage() {/** @type {?} */const simpleChangesStore = getSimpleChangesStore(this);/** @type {?} */const current = simpleChangesStore && simpleChangesStore.current;if (current) {/** @type {?} */const previous = (/** @type {?} */ (simpleChangesStore)).previous;if (previous === EMPTY_OBJ) {(/** @type {?} */ (simpleChangesStore)).previous = current;}else {// New changes are copied to the previous store, so that we don't lose history for inputs// which were not changed this timefor (let key in current) {previous[key] = current[key];}}(/** @type {?} */ (simpleChangesStore)).current = null;this.ngOnChanges(current);}});
}

/*** Wraps an event listener with a function that marks ancestors dirty and prevents default behavior,* if applicable.** @param {?} tNode The TNode associated with this listener* @param {?} lView The LView that contains this listener* @param {?} listenerFn The listener function to call* @param {?} wrapWithPreventDefault Whether or not to prevent default behavior* (the procedural renderer does this already, so in those cases, we should skip)* @return {?}*/
function wrapListener(tNode, lView, listenerFn, wrapWithPreventDefault) {// Note: we are performing most of the work in the listener function itself// to optimize listener registration.return (/*** @param {?} e* @return {?}*/function wrapListenerIn_markDirtyAndPreventDefault(e) {// Ivy uses `Function` as a special token that allows us to unwrap the function// so that it can be invoked programmatically by `DebugNode.triggerEventHandler`.if (e === Function) {return listenerFn;}// In order to be backwards compatible with View Engine, events on component host nodes// must also mark the component view itself dirty (i.e. the view that it owns)./** @type {?} */const startView = tNode.flags & 2 /* isComponentHost */ ?getComponentLViewByIndex(tNode.index, lView) :lView;// See interfaces/view.ts for more on LViewFlags.ManualOnPushif ((lView[FLAGS] & 32 /* ManualOnPush */) === 0) {markViewDirty(startView);}/** @type {?} */let result = executeListenerWithErrorHandling(lView, listenerFn, e);// A just-invoked listener function might have coalesced listeners so we need to check for// their presence and invoke as needed./** @type {?} */let nextListenerFn = ((/** @type {?} */ (wrapListenerIn_markDirtyAndPreventDefault))).__ngNextListenerFn__;while (nextListenerFn) {// We should prevent default if any of the listeners explicitly return falseresult = executeListenerWithErrorHandling(lView, nextListenerFn, e) && result;nextListenerFn = ((/** @type {?} */ (nextListenerFn))).__ngNextListenerFn__;}if (wrapWithPreventDefault && result === false) {e.preventDefault();// Necessary for legacy browsers that don't support preventDefault (e.g. IE)e.returnValue = false;}return result;});
}

来到forms.js的_handleInput(value):


要获取更多Jerry的原创文章,请关注公众号"汪子熙":

通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理相关推荐

  1. SAP Cloud for Customer(C4C) HTML mashup的运行时单步调试

    2018-03-22 6:17PM - yongda

  2. C#,入门教程(32)——程序运行时的调试技巧与逻辑错误探针技术与源代码

    上一篇: C#,入门教程(31)--预处理指令的基础知识与使用方法https://blog.csdn.net/beijinghorn/article/details/124205075 一.关于程序错 ...

  3. 双向绑定 当obj的值修改时_JavaScript进阶之深入理解数据双向绑定

    前言 谈起当前前端最热门的 js 框架,必少不了 Vue.React.Angular,对于大多数人来说,我们更多的是在使用框架,对于框架解决痛点背后使用的基本原理往往关注不多,近期在研读 Vue.js ...

  4. 大数据_Flink_数据处理_运行时架构2_作业提交流程_抽象架构---Flink工作笔记0017

    1.首先先去提交任务,这个提交任务可以是在网页端后台,也可以是在命令行提交任务. 在网页端后台提交就是flink的网页管理端对吧. 2.第1步任务将会被提交到分发器 3.第2步分发器会把任务转交给jo ...

  5. 固态硬盘运行服务器,固态硬盘(SSD)在服务器中的工作原理是什么

    所有计算机都需要一种方法来存储.检索和共享数字信息,这通常是通过硬盘驱动器完成的.硬盘驱动器使用一个或多个快速旋转的磁盘或涂有磁性材料的盘片磁性存储数据.最早的硬盘非常大而且非常昂贵. 目前,有两种主 ...

  6. matlab2013单步运行,matlab如何单步调试

    (5)断点设置.断点为 MATLAB 程序执行时人为设置的中断点,程序运行至断点... 在MATLAB下,利用File菜单中的Open或 New命令,打开已建的M文件或新建M文 件,这时在MATLAB ...

  7. 快速搭建本地 .NET Core 运行时调试环境

    需要的软件环境: Oracle VM VirtualBox CentOS 7 llvm lldb 3.6.0 (3.5.0我试过 dumpobj时候一直报无效参数 Invalid parameter ...

  8. linux下gdb单步调试

    用 GDB调试程序 GDB 概述 ---- GDB 是 GNU开源组织发布的一个强大的 UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像 VC. BCB等 IDE的调试,但如果你是在 ...

  9. java slf4j日志级别_java - 在slf4j中设置运行时消息的日志级别 - 堆栈内存溢出

    ===============>>#1 票数:41 已采纳 使用slf4j无法做到这slf4j . 我想,缺少这个功能的原因是,几乎不可能为slf4j构建一个Level类型,它可以有效地映 ...

最新文章

  1. 下一代超大规模软件定义网络技术实践
  2. mysql中文乱码问题的解决方案
  3. ldd 3 重定向打印开启 misc-progs
  4. 华科与浙大计算机学院,计算机最强14所高校排名,清华第2,浙大第4,南大第6,华科第10...
  5. 使用Java语言从零开始创建区块链
  6. linux redhat5.5终端打不开,为什么在 RedHat Linux 5 下不能使用 ifconfig 命令
  7. 用xfire开发webservice实例
  8. Java多线程之线程虚假唤醒
  9. android 复制u盘文件到手机本地_如何导出Android中的文件(把Android当做U盘)
  10. 从零开始学PowerShell(9)创建PowerShell对象
  11. JimuReport积木报表,一个好用的开源免费的报表平台!
  12. MyEclipse从数据库反向生成实体类通过Hibernate的方式----mysql数据库实例
  13. 周黑鸭借力MES,推进智能生产
  14. 漫步者头戴式蓝牙耳机一边有声音一边没声音
  15. Flash遮罩之溜光字制作一
  16. 【艾特淘】直通车数据化选款技巧
  17. Ps钢笔工具及其附属工具的用法
  18. 金玉良缘易配而木石前盟难得|M1 Mac os(Apple Silicon)天生一对Python3开发环境搭建(集成深度学习框架Tensorflow/Pytorch)
  19. 4g能用吗64java,4g内存能不能装64位系统吗_电脑内存是4g可以安装64位系统吗
  20. element ui vue el-input el-button 键盘回车事件

热门文章

  1. 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]
  2. su的时候密码认证失败的解决方法
  3. UML模型中的图-用例图
  4. Rabbitmq集群高可用部署详细
  5. Android学习之基础知识四-Activity活动7讲(活动的启动模式)
  6. 【NOIP2014】子矩阵
  7. 06_排序_希尔排序
  8. 在openstack环境中安装rackspace private cloud --1 环境准备
  9. 通信控件MSComm使用详解
  10. 【面试相关】python实现快速幂取余算法详解