From b9ee8b46a0624d6f3caf7741e45dd35912b4b6ab Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 18 Sep 2017 17:28:34 -0700 Subject: [PATCH] refactor(core): viewDef related refactoring (#19272) - optimize the way node flags are propagated in `viewDef()`, - fix `elementDef()` signature to make `namespaceAndName` nullable, - move render parent computation with the parent computation PR Close #19272 --- packages/core/src/view/element.ts | 2 +- packages/core/src/view/types.ts | 1 + packages/core/src/view/view.ts | 98 +++++++++++++++++-------------- 3 files changed, 56 insertions(+), 45 deletions(-) diff --git a/packages/core/src/view/element.ts b/packages/core/src/view/element.ts index 0f24d4c7d9..34e7642045 100644 --- a/packages/core/src/view/element.ts +++ b/packages/core/src/view/element.ts @@ -55,7 +55,7 @@ export function anchorDef( export function elementDef( flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], - ngContentIndex: number, childCount: number, namespaceAndName: string, + ngContentIndex: number, childCount: number, namespaceAndName: string | null, fixedAttrs: [string, string][] = [], bindings?: [BindingFlags, string, string | SecurityContext][], outputs?: ([string, string])[], handleEvent?: ElementHandleEventFn, componentView?: ViewDefinitionFactory, diff --git a/packages/core/src/view/types.ts b/packages/core/src/view/types.ts index 2e1fcc67b6..8a123773ee 100644 --- a/packages/core/src/view/types.ts +++ b/packages/core/src/view/types.ts @@ -233,6 +233,7 @@ export const enum QueryValueType { } export interface ElementDef { + // set to null for `` name: string|null; ns: string|null; /** ns, name, value */ diff --git a/packages/core/src/view/view.ts b/packages/core/src/view/view.ts index 2bc62aae47..f2f63686f0 100644 --- a/packages/core/src/view/view.ts +++ b/packages/core/src/view/view.ts @@ -30,34 +30,21 @@ export function viewDef( let viewRootNodeFlags = 0; let viewMatchedQueries = 0; let currentParent: NodeDef|null = null; + let currentRenderParent: NodeDef|null = null; let currentElementHasPublicProviders = false; let currentElementHasPrivateProviders = false; let lastRenderRootNode: NodeDef|null = null; for (let i = 0; i < nodes.length; i++) { - while (currentParent && i > currentParent.index + currentParent.childCount) { - const newParent: NodeDef|null = currentParent.parent; - if (newParent) { - newParent.childFlags |= currentParent.childFlags !; - newParent.childMatchedQueries |= currentParent.childMatchedQueries; - } - currentParent = newParent; - } const node = nodes[i]; node.index = i; node.parent = currentParent; node.bindingIndex = viewBindingCount; node.outputIndex = viewDisposableCount; - - // renderParent needs to account for ng-container! - let currentRenderParent: NodeDef|null; - if (currentParent && currentParent.flags & NodeFlags.TypeElement && - !currentParent.element !.name) { - currentRenderParent = currentParent.renderParent; - } else { - currentRenderParent = currentParent; - } node.renderParent = currentRenderParent; + viewNodeFlags |= node.flags; + viewMatchedQueries |= node.matchedQueryIds; + if (node.element) { const elDef = node.element; elDef.publicProviders = @@ -66,24 +53,13 @@ export function viewDef( // Note: We assume that all providers of an element are before any child element! currentElementHasPublicProviders = false; currentElementHasPrivateProviders = false; + + if (node.element.template) { + viewMatchedQueries |= node.element.template.nodeMatchedQueries; + } } validateNode(currentParent, node, nodes.length); - viewNodeFlags |= node.flags; - viewMatchedQueries |= node.matchedQueryIds; - if (node.element && node.element.template) { - viewMatchedQueries |= node.element.template.nodeMatchedQueries; - } - if (currentParent) { - currentParent.childFlags |= node.flags; - currentParent.directChildFlags |= node.flags; - currentParent.childMatchedQueries |= node.matchedQueryIds; - if (node.element && node.element.template) { - currentParent.childMatchedQueries |= node.element.template.nodeMatchedQueries; - } - } else { - viewRootNodeFlags |= node.flags; - } viewBindingCount += node.bindings.length; viewDisposableCount += node.outputs.length; @@ -91,6 +67,7 @@ export function viewDef( if (!currentRenderParent && (node.flags & NodeFlags.CatRenderNode)) { lastRenderRootNode = node; } + if (node.flags & NodeFlags.CatProvider) { if (!currentElementHasPublicProviders) { currentElementHasPublicProviders = true; @@ -106,7 +83,7 @@ export function viewDef( } else { if (!currentElementHasPrivateProviders) { currentElementHasPrivateProviders = true; - // Use protoyypical inheritance to not get O(n^2) complexity... + // Use prototypical inheritance to not get O(n^2) complexity... currentParent !.element !.allProviders = Object.create(currentParent !.element !.publicProviders); } @@ -116,20 +93,50 @@ export function viewDef( currentParent !.element !.componentProvider = node; } } - if (node.childCount) { + + if (currentParent) { + currentParent.childFlags |= node.flags; + currentParent.directChildFlags |= node.flags; + currentParent.childMatchedQueries |= node.matchedQueryIds; + if (node.element && node.element.template) { + currentParent.childMatchedQueries |= node.element.template.nodeMatchedQueries; + } + } else { + viewRootNodeFlags |= node.flags; + } + + if (node.childCount > 0) { currentParent = node; + + if (!isNgContainer(node)) { + currentRenderParent = node; + } + } else { + // When the current node has no children, check if it is the last children of its parent. + // When it is, propagate the flags up. + // The loop is required because an element could be the last transitive children of several + // elements. We loop to either the root or the highest opened element (= with remaining + // children) + while (currentParent && i === currentParent.index + currentParent.childCount) { + const newParent: NodeDef|null = currentParent.parent; + if (newParent) { + newParent.childFlags |= currentParent.childFlags; + newParent.childMatchedQueries |= currentParent.childMatchedQueries; + } + currentParent = newParent; + // We also need to update the render parent & account for ng-container + if (currentParent && isNgContainer(currentParent)) { + currentRenderParent = currentParent.renderParent; + } else { + currentRenderParent = currentParent; + } + } } } - while (currentParent) { - const newParent = currentParent.parent; - if (newParent) { - newParent.childFlags |= currentParent.childFlags; - newParent.childMatchedQueries |= currentParent.childMatchedQueries; - } - currentParent = newParent; - } + const handleEvent: ViewHandleEventFn = (view, nodeIndex, eventName, event) => nodes[nodeIndex].element !.handleEvent !(view, eventName, event); + return { // Will be filled later... factory: null, @@ -138,13 +145,16 @@ export function viewDef( nodeMatchedQueries: viewMatchedQueries, flags, nodes: nodes, updateDirectives: updateDirectives || NOOP, - updateRenderer: updateRenderer || NOOP, - handleEvent: handleEvent || NOOP, + updateRenderer: updateRenderer || NOOP, handleEvent, bindingCount: viewBindingCount, outputCount: viewDisposableCount, lastRenderRootNode }; } +function isNgContainer(node: NodeDef): boolean { + return (node.flags & NodeFlags.TypeElement) !== 0 && node.element !.name === null; +} + function validateNode(parent: NodeDef | null, node: NodeDef, nodeCount: number) { const template = node.element && node.element.template; if (template) {