fix(ivy): process creation mode deeply before running update mode (#27744)

Prior to this commit, we had two different modes for change detection
execution for Ivy, depending on whether you called `bootstrap()` or
`renderComponent()`. In the former case, we would complete creation
mode for all components in the tree before beginning update mode for
any component. In the latter case, we would run creation mode and
update mode together for each component individually.

Maintaining code to support these two different execution orders was
unnecessarily complex, so this commit aligns the two bootstrapping
mechanisms to execute in the same order. Now creation mode always
runs for all components before update mode begins.

This change also simplifies our rendering logic so that we use
`LView` flags as the source of truth for rendering mode instead of
`rf` function arguments. This fixed some related bugs (e.g. calling
`ViewRef.detectChanges` synchronously after the view's creation
would create view nodes twice, view queries would execute twice, etc).

PR Close #27744
This commit is contained in:
Kara Erickson
2018-12-18 16:58:51 -08:00
committed by Matias Niemelä
parent f48a00fb0c
commit a20b2f72f2
25 changed files with 384 additions and 303 deletions

View File

@ -186,14 +186,9 @@ export function getOrCreateCurrentQueries(
return currentQueries || (lView[QUERIES] = new QueryType(null, null, null));
}
/**
* This property gets set before entering a template.
*/
let creationMode: boolean;
export function getCreationMode(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return creationMode;
/** Checks whether a given view is in creation mode */
export function isCreationMode(view: LView = lView): boolean {
return (view[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode;
}
/**
@ -276,8 +271,6 @@ export function enterView(newView: LView, hostTNode: TElementNode | TViewNode |
const oldView = lView;
if (newView) {
const tView = newView[TVIEW];
creationMode = (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode;
firstTemplatePass = tView.firstTemplatePass;
bindingRootIndex = tView.bindingStartIndex;
}
@ -320,19 +313,17 @@ export function resetComponentState() {
* the direction of traversal (up or down the view tree) a bit clearer.
*
* @param newView New state to become active
* @param creationOnly An optional boolean to indicate that the view was processed in creation mode
* only, i.e. the first update will be done later. Only possible for dynamically created views.
*/
export function leaveView(newView: LView, creationOnly?: boolean): void {
export function leaveView(newView: LView): void {
const tView = lView[TVIEW];
if (!creationOnly) {
if (!checkNoChangesMode) {
executeHooks(lView, tView.viewHooks, tView.viewCheckHooks, creationMode);
}
if (isCreationMode(lView)) {
lView[FLAGS] &= ~LViewFlags.CreationMode;
} else {
executeHooks(lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode);
// Views are clean and in update mode after being checked, so these bits are cleared
lView[FLAGS] &= ~(LViewFlags.CreationMode | LViewFlags.Dirty);
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
lView[FLAGS] |= LViewFlags.RunInit;
lView[BINDING_INDEX] = tView.bindingStartIndex;
}
lView[FLAGS] |= LViewFlags.RunInit;
lView[BINDING_INDEX] = tView.bindingStartIndex;
enterView(newView, null);
}