perf(ivy): avoid repeated native node retrieval and patching (#33322)

Before this change instantiating multiple directives on the same
host node would result in repeated RNode retrieval and patching.
This commint re-organises code around directive instance creation
so the host node processing (common to all directives) happens
once and only once.

As the additional benefit the directive instantiation logic gets
centralised in one function (at the expense of patching logic
duplication for root node).

PR Close #33322
This commit is contained in:
Pawel Kozlowski 2019-10-21 10:38:21 +02:00 committed by atscott
parent 2c208f98a9
commit 41caafcaf2
3 changed files with 18 additions and 36 deletions

View File

@ -24,7 +24,7 @@ import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/c
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector'; import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node'; import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer'; import {RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization'; import {SanitizerFn} from '../interfaces/sanitization';
import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view'; import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
@ -529,10 +529,9 @@ export function executeContentQueries(tView: TView, tNode: TNode, lView: LView)
/** /**
* Creates directive instances. * Creates directive instances.
*/ */
export function createDirectivesInstances( export function createDirectivesInstances(tView: TView, lView: LView, tNode: TDirectiveHostNode) {
tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode) {
if (!getBindingsEnabled()) return; if (!getBindingsEnabled()) return;
instantiateAllDirectives(tView, lView, tNode); instantiateAllDirectives(tView, lView, tNode, getNativeByTNode(tNode, lView));
if ((tNode.flags & TNodeFlags.hasHostBindings) === TNodeFlags.hasHostBindings) { if ((tNode.flags & TNodeFlags.hasHostBindings) === TNodeFlags.hasHostBindings) {
invokeDirectivesHostBindings(tView, lView, tNode); invokeDirectivesHostBindings(tView, lView, tNode);
} }
@ -543,7 +542,8 @@ export function createDirectivesInstances(
* to LView in the same order as they are loaded in the template with load(). * to LView in the same order as they are loaded in the template with load().
*/ */
export function saveResolvedLocalsInData( export function saveResolvedLocalsInData(
viewData: LView, tNode: TNode, localRefExtractor: LocalRefExtractor = getNativeByTNode): void { viewData: LView, tNode: TDirectiveHostNode,
localRefExtractor: LocalRefExtractor = getNativeByTNode): void {
const localNames = tNode.localNames; const localNames = tNode.localNames;
if (localNames) { if (localNames) {
let localIndex = tNode.index + 1; let localIndex = tNode.index + 1;
@ -1018,17 +1018,20 @@ function warnAboutUnknownProperty(propName: string, tNode: TNode): void {
/** /**
* Instantiate a root component. * Instantiate a root component.
*/ */
export function instantiateRootComponent<T>( export function instantiateRootComponent<T>(tView: TView, lView: LView, def: ComponentDef<T>): T {
tView: TView, viewData: LView, def: ComponentDef<T>): T {
const rootTNode = getPreviousOrParentTNode(); const rootTNode = getPreviousOrParentTNode();
if (tView.firstTemplatePass) { if (tView.firstTemplatePass) {
if (def.providersResolver) def.providersResolver(def); if (def.providersResolver) def.providersResolver(def);
generateExpandoInstructionBlock(tView, rootTNode, 1); generateExpandoInstructionBlock(tView, rootTNode, 1);
baseResolveDirective(tView, viewData, def); baseResolveDirective(tView, lView, def);
} }
const directive = const directive =
getNodeInjectable(tView.data, viewData, viewData.length - 1, rootTNode as TElementNode); getNodeInjectable(tView.data, lView, lView.length - 1, rootTNode as TElementNode);
postProcessBaseDirective(viewData, rootTNode, directive); attachPatchData(directive, lView);
const native = getNativeByTNode(rootTNode, lView);
if (native) {
attachPatchData(native, lView);
}
return directive; return directive;
} }
@ -1090,13 +1093,16 @@ export function resolveDirectives(
/** /**
* Instantiate all the directives that were previously resolved on the current node. * Instantiate all the directives that were previously resolved on the current node.
*/ */
function instantiateAllDirectives(tView: TView, lView: LView, tNode: TDirectiveHostNode) { function instantiateAllDirectives(
tView: TView, lView: LView, tNode: TDirectiveHostNode, native: RNode) {
const start = tNode.directiveStart; const start = tNode.directiveStart;
const end = tNode.directiveEnd; const end = tNode.directiveEnd;
if (!tView.firstTemplatePass) { if (!tView.firstTemplatePass) {
getOrCreateNodeInjectorForNode(tNode, lView); getOrCreateNodeInjectorForNode(tNode, lView);
} }
attachPatchData(native, lView);
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
const def = tView.data[i] as DirectiveDef<any>; const def = tView.data[i] as DirectiveDef<any>;
const isComponent = isComponentDef(def); const isComponent = isComponentDef(def);
@ -1107,8 +1113,8 @@ function instantiateAllDirectives(tView: TView, lView: LView, tNode: TDirectiveH
} }
const directive = getNodeInjectable(tView.data, lView, i, tNode); const directive = getNodeInjectable(tView.data, lView, i, tNode);
attachPatchData(directive, lView);
postProcessBaseDirective(lView, tNode, directive);
if (tNode.initialInputs !== null) { if (tNode.initialInputs !== null) {
setInputsFromAttrs(lView, i - start, directive, def, tNode); setInputsFromAttrs(lView, i - start, directive, def, tNode);
} }
@ -1182,21 +1188,6 @@ export function generateExpandoInstructionBlock(
])).push(elementIndex, providerCount, directiveCount); ])).push(elementIndex, providerCount, directiveCount);
} }
/**
* A lighter version of postProcessDirective() that is used for the root component.
*/
function postProcessBaseDirective<T>(lView: LView, hostTNode: TNode, directive: T): void {
ngDevMode && assertLessThanOrEqual(
getBindingIndex(), lView[TVIEW].bindingStartIndex,
'directives should be created before any bindings');
attachPatchData(directive, lView);
const native = getNativeByTNode(hostTNode, lView);
if (native) {
attachPatchData(native, lView);
}
}
/** /**
* Matches the current node against all available selectors. * Matches the current node against all available selectors.
* If a component is matched (at most one), it is returned in first position in the array. * If a component is matched (at most one), it is returned in first position in the array.

View File

@ -392,9 +392,6 @@
{ {
"name": "noSideEffects" "name": "noSideEffects"
}, },
{
"name": "postProcessBaseDirective"
},
{ {
"name": "refreshChildComponents" "name": "refreshChildComponents"
}, },

View File

@ -1088,12 +1088,6 @@
{ {
"name": "patchConfig" "name": "patchConfig"
}, },
{
"name": "postProcessBaseDirective"
},
{
"name": "postProcessDirective"
},
{ {
"name": "readPatchedData" "name": "readPatchedData"
}, },