perf(ivy): attempt rendering initial styling only if present (#32979)

PR Close #32979
This commit is contained in:
Pawel Kozlowski 2019-10-03 15:02:10 +02:00 committed by Alex Rickabaugh
parent e6881b5b42
commit 60047037a3
7 changed files with 44 additions and 15 deletions

View File

@ -152,10 +152,6 @@ export function getOrCreateNodeInjectorForNode(
insertBloom(tView.data, tNode); // foundation for node bloom insertBloom(tView.data, tNode); // foundation for node bloom
insertBloom(hostView, null); // foundation for cumulative bloom insertBloom(hostView, null); // foundation for cumulative bloom
insertBloom(tView.blueprint, null); insertBloom(tView.blueprint, null);
ngDevMode && assertEqual(
tNode.flags === 0 || tNode.flags === TNodeFlags.isComponentHost, true,
'expected tNode.flags to not be initialized');
} }
const parentLoc = getParentInjectorLocation(tNode, hostView); const parentLoc = getParentInjectorLocation(tNode, hostView);

View File

@ -10,7 +10,7 @@ import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
import {assertHasParent} from '../assert'; import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery'; import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks'; import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TNodeType} from '../interfaces/node'; import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement} from '../interfaces/renderer'; import {RElement} from '../interfaces/renderer';
import {StylingMapArray, TStylingContext} from '../interfaces/styling'; import {StylingMapArray, TStylingContext} from '../interfaces/styling';
import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks';
@ -64,7 +64,9 @@ export function ɵɵelementStart(
} }
} }
renderInitialStyling(renderer, native, tNode); if ((tNode.flags & TNodeFlags.hasInitialStyling) === TNodeFlags.hasInitialStyling) {
renderInitialStyling(renderer, native, tNode);
}
appendChild(native, tNode, lView); appendChild(native, tNode, lView);

View File

@ -162,6 +162,7 @@ export const TNodeConstructor = class TNode implements ITNode {
if (this.flags & TNodeFlags.hasClassInput) flags.push('TNodeFlags.hasClassInput'); if (this.flags & TNodeFlags.hasClassInput) flags.push('TNodeFlags.hasClassInput');
if (this.flags & TNodeFlags.hasContentQuery) flags.push('TNodeFlags.hasContentQuery'); if (this.flags & TNodeFlags.hasContentQuery) flags.push('TNodeFlags.hasContentQuery');
if (this.flags & TNodeFlags.hasStyleInput) flags.push('TNodeFlags.hasStyleInput'); if (this.flags & TNodeFlags.hasStyleInput) flags.push('TNodeFlags.hasStyleInput');
if (this.flags & TNodeFlags.hasInitialStyling) flags.push('TNodeFlags.hasInitialStyling');
if (this.flags & TNodeFlags.isComponentHost) flags.push('TNodeFlags.isComponentHost'); if (this.flags & TNodeFlags.isComponentHost) flags.push('TNodeFlags.isComponentHost');
if (this.flags & TNodeFlags.isDirectiveHost) flags.push('TNodeFlags.isDirectiveHost'); if (this.flags & TNodeFlags.isDirectiveHost) flags.push('TNodeFlags.isDirectiveHost');
if (this.flags & TNodeFlags.isDetached) flags.push('TNodeFlags.isDetached'); if (this.flags & TNodeFlags.isDetached) flags.push('TNodeFlags.isDetached');

View File

@ -1232,7 +1232,7 @@ function findDirectiveMatches(
*/ */
export function markAsComponentHost(tView: TView, hostTNode: TNode): void { export function markAsComponentHost(tView: TView, hostTNode: TNode): void {
ngDevMode && assertFirstTemplatePass(tView); ngDevMode && assertFirstTemplatePass(tView);
hostTNode.flags = TNodeFlags.isComponentHost; hostTNode.flags |= TNodeFlags.isComponentHost;
(tView.components || (tView.components = ngDevMode ? new TViewComponents() : [ (tView.components || (tView.components = ngDevMode ? new TViewComponents() : [
])).push(hostTNode.index); ])).push(hostTNode.index);
} }
@ -1279,16 +1279,11 @@ function saveNameToExportMap(
* @param index the initial index * @param index the initial index
*/ */
export function initNodeFlags(tNode: TNode, index: number, numberOfDirectives: number) { export function initNodeFlags(tNode: TNode, index: number, numberOfDirectives: number) {
const flags = tNode.flags;
ngDevMode && assertEqual(
flags === 0 || flags === TNodeFlags.isComponentHost, true,
'expected node flags to not be initialized');
ngDevMode && assertNotEqual( ngDevMode && assertNotEqual(
numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, numberOfDirectives, tNode.directiveEnd - tNode.directiveStart,
'Reached the max number of directives'); 'Reached the max number of directives');
tNode.flags |= TNodeFlags.isDirectiveHost;
// When the first directive is created on a node, save the index // When the first directive is created on a node, save the index
tNode.flags = (flags & TNodeFlags.isComponentHost) | TNodeFlags.isDirectiveHost;
tNode.directiveStart = index; tNode.directiveStart = index;
tNode.directiveEnd = index + numberOfDirectives; tNode.directiveEnd = index + numberOfDirectives;
tNode.providerIndexes = index; tNode.providerIndexes = index;

View File

@ -8,7 +8,7 @@
import {SafeValue} from '../../sanitization/bypass'; import {SafeValue} from '../../sanitization/bypass';
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {setInputsForProperty} from '../instructions/shared'; import {setInputsForProperty} from '../instructions/shared';
import {AttributeMarker, TAttributes, TNode, TNodeType} from '../interfaces/node'; import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement} from '../interfaces/renderer'; import {RElement} from '../interfaces/renderer';
import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from '../interfaces/styling'; import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from '../interfaces/styling';
import {BINDING_INDEX, LView, RENDERER} from '../interfaces/view'; import {BINDING_INDEX, LView, RENDERER} from '../interfaces/view';
@ -474,6 +474,10 @@ export function registerInitialStylingOnTNode(
updateRawValueOnContext(tNode.styles, stylingMapToString(styles, false)); updateRawValueOnContext(tNode.styles, stylingMapToString(styles, false));
} }
if (hasAdditionalInitialStyling) {
tNode.flags |= TNodeFlags.hasInitialStyling;
}
return hasAdditionalInitialStyling; return hasAdditionalInitialStyling;
} }

View File

@ -65,8 +65,11 @@ export const enum TNodeFlags {
/** This bit is set if the node has any "style" inputs */ /** This bit is set if the node has any "style" inputs */
hasStyleInput = 0b00100000, hasStyleInput = 0b00100000,
/** This bit is set if the node has initial styling */
hasInitialStyling = 0b01000000,
/** This bit is set if the node has been detached by i18n */ /** This bit is set if the node has been detached by i18n */
isDetached = 0b01000000, isDetached = 0b10000000,
} }
/** /**

View File

@ -113,6 +113,34 @@ describe('styling', () => {
expect(outer.textContent.trim()).toEqual('outer'); expect(outer.textContent.trim()).toEqual('outer');
}); });
it('should render initial styling for repeated nodes that a component host', () => {
@Component({
selector: '[comp]',
template: '',
})
class Comp {
}
@Component({
template: `
<ng-template ngFor [ngForOf]="items" let-item>
<p comp class="a">A</p>
</ng-template>
`
})
class App {
items = [1, 2, 3];
}
TestBed.configureTestingModule({
declarations: [App, Comp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('.a')).length).toBe(3);
});
it('should do nothing for empty style bindings', () => { it('should do nothing for empty style bindings', () => {
@Component({template: '<div [style.color]></div>'}) @Component({template: '<div [style.color]></div>'})
class App { class App {