我在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 time
for (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.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;
});
}来到forms.js的_handleInput(value):