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
This commit is contained in:
Victor Berchet 2017-09-18 17:28:34 -07:00 committed by Igor Minar
parent f2bad195bc
commit abceaa2f33
3 changed files with 56 additions and 45 deletions

View File

@ -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,

View File

@ -233,6 +233,7 @@ export const enum QueryValueType {
}
export interface ElementDef {
// set to null for `<ng-container>`
name: string|null;
ns: string|null;
/** ns, name, value */

View File

@ -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) {