fix(compiler): don’t call `check` if we don’t need to (#15322)

If a directive has not bindings nor has a `ngDoCheck` / `ngOnInit`
lifecycle hook, don’t generate a `check` call.

This does not have an impact on the behavior, but produces
less code.

PR Close #15322
This commit is contained in:
Tobias Bosch 2017-03-20 15:26:06 -07:00 committed by Miško Hevery
parent 9bf2fb4a74
commit 764e90f9bb
2 changed files with 43 additions and 7 deletions

View File

@ -108,6 +108,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
private nodes: (() => {
sourceSpan: ParseSourceSpan,
nodeDef: o.Expression,
directive?: CompileTypeMetadata,
updateDirectives?: UpdateExpression[],
updateRenderer?: UpdateExpression[]
})[] = [];
@ -582,6 +583,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR
]),
updateDirectives: updateDirectiveExpressions,
directive: dirAst.directive.type,
});
return {hostBindings, hostEvents};
@ -790,13 +792,14 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
const updateRendererStmts: o.Statement[] = [];
const updateDirectivesStmts: o.Statement[] = [];
const nodeDefExprs = this.nodes.map((factory, nodeIndex) => {
const {nodeDef, updateDirectives, updateRenderer, sourceSpan} = factory();
const {nodeDef, directive, updateDirectives, updateRenderer, sourceSpan} = factory();
if (updateRenderer) {
updateRendererStmts.push(...createUpdateStatements(nodeIndex, sourceSpan, updateRenderer));
updateRendererStmts.push(
...createUpdateStatements(nodeIndex, sourceSpan, updateRenderer, null));
}
if (updateDirectives) {
updateDirectivesStmts.push(
...createUpdateStatements(nodeIndex, sourceSpan, updateDirectives));
...createUpdateStatements(nodeIndex, sourceSpan, updateDirectives, directive));
}
// We use a comma expression to call the log function before
// the nodeDef function, but still use the result of the nodeDef function
@ -807,8 +810,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
return {updateRendererStmts, updateDirectivesStmts, nodeDefExprs};
function createUpdateStatements(
nodeIndex: number, sourceSpan: ParseSourceSpan,
expressions: UpdateExpression[]): o.Statement[] {
nodeIndex: number, sourceSpan: ParseSourceSpan, expressions: UpdateExpression[],
directive: CompileTypeMetadata): o.Statement[] {
const updateStmts: o.Statement[] = [];
const exprs = expressions.map(({sourceSpan, context, value}) => {
const bindingId = `${updateBindingCount++}`;
@ -819,8 +822,12 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
...stmts.map(stmt => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
return o.applySourceSpanToExpressionIfNeeded(currValExpr, sourceSpan);
});
if (expressions.length ||
(directive && (directive.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1 ||
directive.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1))) {
updateStmts.push(o.applySourceSpanToStatementIfNeeded(
callCheckStmt(nodeIndex, exprs).toStmt(), sourceSpan));
}
return updateStmts;
}
}

View File

@ -339,6 +339,35 @@ describe('compiler (unbundled Angular)', () => {
});
}));
});
describe('generated templates', () => {
it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit',
async(() => {
const FILES: MockData = {
app: {
'app.ts': `
import { NgModule, Component } from '@angular/core';
@Component({ template: '' })
export class AppComponent {}
@NgModule({ declarations: [ AppComponent ] })
export class AppModule { }
`
}
};
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
const aotHost = new MockAotCompilerHost(host);
const genFilePreamble = '/* Hello world! */';
compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics, {genFilePreamble})
.then((generatedFiles) => {
const genFile = generatedFiles.find(
gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
expect(genFile.source).not.toContain('check(');
});
}));
});
});
describe('compiler (bundled Angular)', () => {