refactor(ivy): replace LView.child with TView.childIndex lookup (#24211)

PR Close #24211
This commit is contained in:
Kara Erickson
2018-05-30 13:43:14 -07:00
committed by Victor Berchet
parent 6a663a4073
commit 7e3f8f77a9
6 changed files with 53 additions and 29 deletions

View File

@ -590,7 +590,7 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainer
lContainerNode.tNode = hostTNode.dynamicContainerNode; lContainerNode.tNode = hostTNode.dynamicContainerNode;
vcRefHost.dynamicLContainerNode = lContainerNode; vcRefHost.dynamicLContainerNode = lContainerNode;
addToViewTree(vcRefHost.view, lContainer); addToViewTree(vcRefHost.view, hostTNode.index as number, lContainer);
di.viewContainerRef = new ViewContainerRef(lContainerNode); di.viewContainerRef = new ViewContainerRef(lContainerNode);
} }

View File

@ -17,7 +17,7 @@ import {CurrentMatchesList, LView, LViewFlags, LifecycleStage, RootContext, TDat
import {AttributeMarker, TAttributes, LContainerNode, LElementNode, LNode, TNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue, TElementNode,} from './interfaces/node'; import {AttributeMarker, TAttributes, LContainerNode, LElementNode, LNode, TNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue, TElementNode,} from './interfaces/node';
import {assertNodeType} from './node_assert'; import {assertNodeType} from './node_assert';
import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode, getChildLNode, getParentLNode} from './node_manipulation'; import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode, getChildLNode, getParentLNode, getLViewChild} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, PipeDefList, PipeDefListOrFactory, RenderFlags} from './interfaces/definition'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, PipeDefList, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
@ -314,7 +314,6 @@ export function createLView<T>(
tView: tView, tView: tView,
cleanup: null, cleanup: null,
renderer: renderer, renderer: renderer,
child: null,
tail: null, tail: null,
next: null, next: null,
bindingStartIndex: -1, bindingStartIndex: -1,
@ -802,6 +801,7 @@ export function createTView(
return { return {
node: null !, node: null !,
data: [], data: [],
childIndex: -1, // Children set in addToViewTree(), if any
directives: null, directives: null,
firstTemplatePass: true, firstTemplatePass: true,
initHooks: null, initHooks: null,
@ -1329,11 +1329,12 @@ function addComponentLogic<T>(index: number, instance: T, def: ComponentDef<T>):
// Only component views should be added to the view tree directly. Embedded views are // Only component views should be added to the view tree directly. Embedded views are
// accessed through their containers because they may be removed / re-added later. // accessed through their containers because they may be removed / re-added later.
const hostView = addToViewTree( const hostView = addToViewTree(
currentView, createLView( currentView, previousOrParentNode.tNode.index as number,
-1, rendererFactory.createRenderer( createLView(
previousOrParentNode.native as RElement, def.rendererType), -1,
tView, null, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rendererFactory.createRenderer(previousOrParentNode.native as RElement, def.rendererType),
getCurrentSanitizer())); tView, null, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways,
getCurrentSanitizer()));
// We need to set the host node/data here because when the component LNode was created, // We need to set the host node/data here because when the component LNode was created,
// we didn't yet know it was a component (just an element). // we didn't yet know it was a component (just an element).
@ -1512,7 +1513,7 @@ export function container(
// Containers are added to the current view tree instead of their embedded views // Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted. // because views can be removed and re-inserted.
addToViewTree(currentView, node.data); addToViewTree(currentView, index, node.data);
createDirectivesAndLocals(localRefs); createDirectivesAndLocals(localRefs);
isParent = false; isParent = false;
@ -1574,7 +1575,7 @@ export function containerRefreshEnd(): void {
} }
function refreshDynamicChildren() { function refreshDynamicChildren() {
for (let current = currentView.child; current !== null; current = current.next) { for (let current = getLViewChild(currentView); current !== null; current = current.next) {
// Note: current can be a LView or a LContainer, but here we are only interested in LContainer. // Note: current can be a LView or a LContainer, but here we are only interested in LContainer.
// The distinction is made because nextIndex and views do not exist on LView. // The distinction is made because nextIndex and views do not exist on LView.
if (isLContainer(current)) { if (isLContainer(current)) {
@ -1921,11 +1922,18 @@ function findComponentHost(lView: LView): LElementNode {
* and call onDestroy callbacks. * and call onDestroy callbacks.
* *
* @param currentView The view where LView or LContainer should be added * @param currentView The view where LView or LContainer should be added
* @param hostIndex Index of the view's host node in data[]
* @param state The LView or LContainer to add to the view tree * @param state The LView or LContainer to add to the view tree
* @returns The state passed in * @returns The state passed in
*/ */
export function addToViewTree<T extends LView|LContainer>(currentView: LView, state: T): T { export function addToViewTree<T extends LView|LContainer>(
currentView.tail ? (currentView.tail.next = state) : (currentView.child = state); currentView: LView, hostIndex: number, state: T): T {
// TODO(kara): move next and tail properties off of LView
if (currentView.tail) {
currentView.tail.next = state;
} else if (firstTemplatePass) {
currentView.tView.childIndex = hostIndex;
}
currentView.tail = state; currentView.tail = state;
return state; return state;
} }

View File

@ -112,17 +112,6 @@ export interface LView {
*/ */
lifecycleStage: LifecycleStage; lifecycleStage: LifecycleStage;
/**
* The first LView or LContainer beneath this LView in the hierarchy.
*
* Necessary to store this so views can traverse through their nested views
* to remove listeners and call onDestroy callbacks.
*
* For embedded views, we store the LContainer rather than the first ViewState
* to avoid managing splicing when views are added/removed.
*/
child: LView|LContainer|null;
/** /**
* The last LView or LContainer beneath this LView in the hierarchy. * The last LView or LContainer beneath this LView in the hierarchy.
* *
@ -222,8 +211,8 @@ export const enum LViewFlags {
/** Interface necessary to work with view tree traversal */ /** Interface necessary to work with view tree traversal */
export interface LViewOrLContainer { export interface LViewOrLContainer {
next: LView|LContainer|null; next: LView|LContainer|null;
child?: LView|LContainer|null;
views?: LViewNode[]; views?: LViewNode[];
tView?: TView;
parent: LView|null; parent: LView|null;
} }
@ -251,6 +240,18 @@ export interface TView {
/** Static data equivalent of LView.data[]. Contains TNodes. */ /** Static data equivalent of LView.data[]. Contains TNodes. */
data: TData; data: TData;
/**
* Index of the host node of the first LView or LContainer beneath this LView in
* the hierarchy.
*
* Necessary to store this so views can traverse through their nested views
* to remove listeners and call onDestroy callbacks.
*
* For embedded views, we store the index of an LContainer's host rather than the first
* LView to avoid managing splicing when views are added/removed.
*/
childIndex: number;
/** /**
* Selector matches for a node are temporarily cached on the TView so the * Selector matches for a node are temporarily cached on the TView so the
* DI system can eagerly instantiate directives on the same node if they are * DI system can eagerly instantiate directives on the same node if they are

View File

@ -261,19 +261,19 @@ export function addRemoveViewFromContainer(
* @param rootView The view to destroy * @param rootView The view to destroy
*/ */
export function destroyViewTree(rootView: LView): void { export function destroyViewTree(rootView: LView): void {
// A view to cleanup doesn't have children so we should not try to descend down the view tree. // If the view has no children, we can clean it up and return early.
if (!rootView.child) { if (rootView.tView.childIndex === -1) {
return cleanUpView(rootView); return cleanUpView(rootView);
} }
let viewOrContainer: LViewOrLContainer|null = rootView.child; let viewOrContainer: LViewOrLContainer|null = getLViewChild(rootView);
while (viewOrContainer) { while (viewOrContainer) {
let next: LViewOrLContainer|null = null; let next: LViewOrLContainer|null = null;
if (viewOrContainer.views && viewOrContainer.views.length) { if (viewOrContainer.views && viewOrContainer.views.length) {
next = viewOrContainer.views[0].data; next = viewOrContainer.views[0].data;
} else if (viewOrContainer.child) { } else if (viewOrContainer.tView && viewOrContainer.tView.childIndex > -1) {
next = viewOrContainer.child; next = getLViewChild(viewOrContainer as LView);
} else if (viewOrContainer.next) { } else if (viewOrContainer.next) {
// Only move to the side and clean if operating below rootView - // Only move to the side and clean if operating below rootView -
// otherwise we would start cleaning up sibling views of the rootView. // otherwise we would start cleaning up sibling views of the rootView.
@ -383,6 +383,15 @@ export function removeView(container: LContainerNode, removeIndex: number): LVie
return viewNode; return viewNode;
} }
/** Gets the child of the given LView */
export function getLViewChild(view: LView): LView|LContainer|null {
if (view.tView.childIndex === -1) return null;
const hostNode: LElementNode|LContainerNode = view.data[view.tView.childIndex];
return hostNode.data ? hostNode.data : (hostNode.dynamicLContainerNode as LContainerNode).data;
}
/** /**
* Determines which LViewOrLContainer to jump to when traversing back up the * Determines which LViewOrLContainer to jump to when traversing back up the
* tree in destroyViewTree. * tree in destroyViewTree.

View File

@ -107,6 +107,9 @@
{ {
"name": "getDirectiveInstance" "name": "getDirectiveInstance"
}, },
{
"name": "getLViewChild"
},
{ {
"name": "getOrCreateTView" "name": "getOrCreateTView"
}, },

View File

@ -389,6 +389,9 @@
{ {
"name": "getDirectiveInstance" "name": "getDirectiveInstance"
}, },
{
"name": "getLViewChild"
},
{ {
"name": "getNextLNode" "name": "getNextLNode"
}, },