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

简介: 通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理

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

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

* 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);

}image.png

函数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 time

               for (let key in current) {

                   previous[key] = current[key];

               }

           }

           (/** @type {?} */ (simpleChangesStore)).current = null;

           this.ngOnChanges(current);

       }

   });

}image.png/**

* 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.ManualOnPush

       if ((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 false

           result = 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;

   });

}image.png来到forms.js的_handleInput(value):image.pngimage.png

相关文章
|
6月前
|
存储 编解码 Windows
LabVIEW程序代码更新缓慢
LabVIEW程序代码更新缓慢
71 1
|
6月前
|
搜索推荐 编译器 开发者
应用程序的运行:原理、过程与代码实践
应用程序的运行:原理、过程与代码实践
188 1
|
6月前
|
存储
LabVIEW状态机迭代时间调试工具
LabVIEW状态机迭代时间调试工具
31 0
|
6月前
|
编译器 C++
我终于体会到了:代码竟然不可以运行,为什么呢?代码竟然可以运行,为什么呢?
我终于体会到了:代码竟然不可以运行,为什么呢?代码竟然可以运行,为什么呢?
73 0
我终于体会到了:代码竟然不可以运行,为什么呢?代码竟然可以运行,为什么呢?
|
Web App开发 JavaScript IDE
如何直观地理解程序的运行过程?
了解代码的执行过程是编程的基本要求。一个熟练的编程老手只需要用肉眼看着代码,就能对其运行的过程有所了解。然而对于刚接触编程不久的新手来说,这种事情就没那么显而易见了。于是在编写代码和调试代码时,一旦程序的逻辑有些复杂,就像掉进代码的迷宫,完全不知道东南西北。
|
JSON JavaScript 数据格式
ng-template 使用过程中默认参数不能按照期望工作的问题单步调试
ng-template 使用过程中默认参数不能按照期望工作的问题单步调试
100 0
ng-template 使用过程中默认参数不能按照期望工作的问题单步调试
通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理
通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理
149 0
通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理
一个基于ngrx的计数器例子 单步调试搞清楚原理
一个基于ngrx的计数器例子 单步调试搞清楚原理
122 0
一个基于ngrx的计数器例子 单步调试搞清楚原理
|
缓存
单步调试学习NgRx createSelector 工具函数的使用方式
单步调试学习NgRx createSelector 工具函数的使用方式
单步调试学习NgRx createSelector 工具函数的使用方式