perf(ivy): move attributes array into component def (#32798)

Currently Ivy stores the element attributes into an array above the component def and passes it into the relevant instructions, however the problem is that upon minification the array will get a unique name which won't compress very well. These changes move the attributes array into the component def and pass in the index into the instructions instead.

Before:
```
const _c0 = ['foo', 'bar'];

SomeComp.ngComponentDef = defineComponent({
  template: function() {
    element(0, 'div', _c0);
  }
});
```

After:
```
SomeComp.ngComponentDef = defineComponent({
  consts: [['foo', 'bar']],
  template: function() {
    element(0, 'div', 0);
  }
});
```

A couple of cases that this PR doesn't handle:
* Template references are still in a separate array.
* i18n attributes are still in a separate array.

PR Close #32798
This commit is contained in:
crisbeto
2019-09-23 20:08:51 +02:00
committed by Alex Rickabaugh
parent b2b917d2d8
commit d5b87d32b0
52 changed files with 912 additions and 885 deletions

View File

@ -240,12 +240,18 @@ export function compileComponentFromMetadata(
definitionMap.set('ngContentSelectors', ngContentSelectors);
}
// e.g. `consts: 2`
definitionMap.set('consts', o.literal(templateBuilder.getConstCount()));
// e.g. `decls: 2`
definitionMap.set('decls', o.literal(templateBuilder.getConstCount()));
// e.g. `vars: 2`
definitionMap.set('vars', o.literal(templateBuilder.getVarCount()));
// e.g. `consts: [['one', 'two'], ['three', 'four']]
const consts = templateBuilder.getConsts();
if (consts.length > 0) {
definitionMap.set('consts', o.literalArr(consts));
}
definitionMap.set('template', templateFunctionExpression);
// e.g. `directives: [MyDirective]`

View File

@ -165,8 +165,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
private templateIndex: number|null, private templateName: string|null,
private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>,
private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>,
private _namespace: o.ExternalReference, private relativeContextFilePath: string,
private i18nUseExternalIds: boolean) {
private _namespace: o.ExternalReference, relativeContextFilePath: string,
private i18nUseExternalIds: boolean, private _constants: o.Expression[] = []) {
this._bindingScope = parentBindingScope.nestedScope(level);
// Turn the relative context file path into an identifier by replacing non-alphanumeric
@ -606,7 +606,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// add attributes for directive and projection matching purposes
attributes.push(...this.prepareNonRenderAttrs(
allOtherInputs, element.outputs, stylingBuilder, [], i18nAttrs, ngProjectAsAttr));
parameters.push(this.toAttrsParam(attributes));
parameters.push(this.addConstants(attributes));
// local refs (ex.: <div #foo #bar="baz">)
parameters.push(this.prepareRefsParameter(element.references));
@ -869,7 +869,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
(a: t.TextAttribute) => { attrsExprs.push(asLiteral(a.name), asLiteral(a.value)); });
attrsExprs.push(...this.prepareNonRenderAttrs(
template.inputs, template.outputs, undefined, template.templateAttrs));
parameters.push(this.toAttrsParam(attrsExprs));
parameters.push(this.addConstants(attrsExprs));
// local refs (ex.: <ng-template #foo>)
if (template.references && template.references.length) {
@ -881,7 +881,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const templateVisitor = new TemplateDefinitionBuilder(
this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n,
templateIndex, templateName, this.directiveMatcher, this.directives, this.pipeTypeByName,
this.pipes, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds);
this.pipes, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds,
this._constants);
// Nested templates must not be visited until after their parent templates have completed
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
@ -1020,6 +1021,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
getVarCount() { return this._pureFunctionSlots; }
getConsts() { return this._constants; }
getNgContentSelectors(): o.Expression|null {
return this._ngContentReservedSlots.length ?
this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) :
@ -1291,10 +1294,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
return attrExprs;
}
private toAttrsParam(attrsExprs: o.Expression[]): o.Expression {
return attrsExprs.length > 0 ?
this.constantPool.getConstLiteral(o.literalArr(attrsExprs), true) :
o.TYPED_NULL_EXPR;
private addConstants(constExprs: o.Expression[]): o.LiteralExpr {
if (constExprs.length > 0) {
const literal = o.literalArr(constExprs);
// Try to reuse a literal that's already in the array, if possible.
for (let i = 0; i < this._constants.length; i++) {
if (this._constants[i].isEquivalent(literal)) {
return o.literal(i);
}
}
return o.literal(this._constants.push(literal) - 1);
}
return o.TYPED_NULL_EXPR;
}
private prepareRefsParameter(references: t.Reference[]): o.Expression {