/** * Processes a view in update mode. This includes a number of steps in a specific order: * - executing a template function in update mode; * - executing hooks; * - refreshing queries; * - setting host bindings; * - refreshing child (embedded and component) views. */ function refreshView(tView, lView, templateFn, context) { ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode'); const flags = lView[FLAGS]; if ((flags & 256 /* Destroyed */) === 256 /* Destroyed */) return; enterView(lView, lView[T_HOST]); const checkNoChangesMode = getCheckNoChangesMode(); try { resetPreOrderHookFlags(lView); setBindingIndex(tView.bindingStartIndex); if (templateFn !== null) { executeTemplate(tView, lView, templateFn, 2 /* Update */, context); } const hooksInitPhaseCompleted = (flags & 3 /* InitPhaseStateMask */) === 3 /* InitPhaseCompleted */; // execute pre-order hooks (OnInit, OnChanges, DoCheck) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!checkNoChangesMode) { if (hooksInitPhaseCompleted) { const preOrderCheckHooks = tView.preOrderCheckHooks; if (preOrderCheckHooks !== null) { executeCheckHooks(lView, preOrderCheckHooks, null); } } else { const preOrderHooks = tView.preOrderHooks; if (preOrderHooks !== null) { executeInitAndCheckHooks(lView, preOrderHooks, 0 /* OnInitHooksToBeRun */, null); } incrementInitPhaseFlags(lView, 0 /* OnInitHooksToBeRun */); } } // First mark transplanted views that are declared in this lView as needing a refresh at their // insertion points. This is needed to avoid the situation where the template is defined in this // `LView` but its declaration appears after the insertion component. markTransplantedViewsForRefresh(lView); refreshEmbeddedViews(lView); // Content query results must be refreshed before content hooks are called. if (tView.contentQueries !== null) { refreshContentQueries(tView, lView); } // execute content hooks (AfterContentInit, AfterContentChecked) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!checkNoChangesMode) { if (hooksInitPhaseCompleted) { const contentCheckHooks = tView.contentCheckHooks; if (contentCheckHooks !== null) { executeCheckHooks(lView, contentCheckHooks); } } else { const contentHooks = tView.contentHooks; if (contentHooks !== null) { executeInitAndCheckHooks(lView, contentHooks, 1 /* AfterContentInitHooksToBeRun */); } incrementInitPhaseFlags(lView, 1 /* AfterContentInitHooksToBeRun */); } } setHostBindingsByExecutingExpandoInstructions(tView, lView); // Refresh child component views. const components = tView.components; if (components !== null) { refreshChildComponents(lView, components); } // View queries must execute after refreshing child components because a template in this view // could be inserted in a child component. If the view query executes before child component // refresh, the template might not yet be inserted. const viewQuery = tView.viewQuery; if (viewQuery !== null) { executeViewQueryFn(2 /* Update */, viewQuery, context); } // execute view hooks (AfterViewInit, AfterViewChecked) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!checkNoChangesMode) { if (hooksInitPhaseCompleted) { const viewCheckHooks = tView.viewCheckHooks; if (viewCheckHooks !== null) { executeCheckHooks(lView, viewCheckHooks); } } else { const viewHooks = tView.viewHooks; if (viewHooks !== null) { executeInitAndCheckHooks(lView, viewHooks, 2 /* AfterViewInitHooksToBeRun */); } incrementInitPhaseFlags(lView, 2 /* AfterViewInitHooksToBeRun */); } } if (tView.firstUpdatePass === true) { // We need to make sure that we only flip the flag on successful `refreshView` only // Don't do this in `finally` block. // If we did this in `finally` block then an exception could block the execution of styling // instructions which in turn would be unable to insert themselves into the styling linked // list. The result of this would be that if the exception would not be throw on subsequent CD // the styling would be unable to process it data and reflect to the DOM. tView.firstUpdatePass = false; } // Do not reset the dirty state when running in check no changes mode. We don't want components // to behave differently depending on whether check no changes is enabled or not. For example: // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to // refresh a `NgClass` binding should work. If we would reset the dirty state in the check // no changes cycle, the component would be not be dirty for the next update pass. This would // be different in production mode where the component dirty state is not reset. if (!checkNoChangesMode) { lView[FLAGS] &= ~(64 /* Dirty */ | 8 /* FirstLViewPass */); } if (lView[FLAGS] & 1024 /* RefreshTransplantedView */) { lView[FLAGS] &= ~1024 /* RefreshTransplantedView */; updateTransplantedViewCount(lView[PARENT], -1); } } finally { leaveView(); } }
从refreshView函数调用hook的顺序来看,ngOnChanges hook的执行一定先于ngAfterViewInit: