refactor(compiler): speed up proto view merging

- Don't create intermediate merge results
- Only merge embedded ProtoViews that contain `<ng-content>` tags

Closes #3150
Closes #3177
This commit is contained in:
Tobias Bosch
2015-07-20 09:59:44 -07:00
parent de18da2a0d
commit 078475a082
20 changed files with 359 additions and 466 deletions
@@ -53,7 +53,7 @@ export function main() {
{componentId: 'someComponent', template: '{{a}}', directives: []})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
tb.renderer.setText(rootView.viewRef, 0, 'hello');
expect(rootView.hostElement).toHaveText('hello');
@@ -92,7 +92,7 @@ export function main() {
expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
};
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
// root element
checkSetters(elRef(rootView.viewRef, 0), rootView.hostElement);
// nested elements
@@ -114,7 +114,7 @@ export function main() {
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
var el = DOM.childNodes(rootView.hostElement)[0];
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
@@ -138,7 +138,7 @@ export function main() {
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
var el = DOM.childNodes(rootView.hostElement)[0];
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
@@ -160,7 +160,7 @@ export function main() {
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
tb.renderer.invokeElementMethod(elRef(rootView.viewRef, 1), 'setAttribute',
['a', 'b']);
@@ -183,7 +183,7 @@ export function main() {
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
var elr = elRef(rootView.viewRef, 1);
expect(rootView.hostElement).toHaveText('');
@@ -208,7 +208,7 @@ export function main() {
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
var elr = elRef(rootView.viewRef, 1);
expect(rootView.hostElement).toHaveText('');
@@ -233,8 +233,8 @@ export function main() {
directives: []
})
])
.then((protoViewDtos) => {
var rootView = tb.createView(protoViewDtos[0]);
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
tb.triggerEvent(elRef(rootView.viewRef, 1), 'change');
var eventEntry = rootView.events[0];
@@ -263,7 +263,7 @@ export function main() {
{componentId: 'someComponent', template: 'hello', directives: []})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings[0]);
var rootView = tb.createView(protoViewMergeMappings);
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('hello');
async.done();
});
@@ -82,14 +82,13 @@ export class DomTestbed {
return PromiseWrapper.all(promises);
}
merge(protoViews:
List<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping[]> {
merge(protoViews: List<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping> {
return this.compiler.mergeProtoViewsRecursively(collectMergeRenderProtoViewsRecurse(
<ProtoViewDto>protoViews[0], ListWrapper.slice(protoViews, 1)));
}
compileAndMerge(host: DirectiveMetadata,
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping[]> {
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping> {
return this.compile(host, componentViews).then(protoViewDtos => this.merge(protoViewDtos));
}
@@ -133,7 +133,7 @@ export function main() {
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<div class="x y"></div>)</b>)</a></root>'
]));
it('should keep non projected embedded views (so that they can be moved manually)',
it('should keep non projected embedded views as fragments (so that they can be moved manually)',
runAndAssert(
'root', ['<a><template class="x">b</template></a>', ''],
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>', 'b']));
@@ -145,13 +145,6 @@ export function main() {
'b'
]));
it('should project embedded views and match the single root element',
runAndAssert(
'root', ['<a><div class="x" *ng-if></div></a>', 'A(<ng-content></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
'<div *ng-if="" class="x"></div>'
]));
it('should project nodes using the ng-content in embedded views',
runAndAssert('root', ['<a>b</a>', 'A(<ng-content *ng-if></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
@@ -258,7 +251,7 @@ function runAndAssert(hostElementName: string, componentTemplates: string[],
directives: [aComp, bComp, cComp]
})))
.then((mergeMappings) => {
expect(stringify(mergeMappings[0])).toEqual(expectedFragments);
expect(stringify(mergeMappings)).toEqual(expectedFragments);
async.done();
});
});
@@ -1,56 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
beforeEachBindings,
SpyObject,
stringifyElement
} from 'angular2/test_lib';
import {mergeSelectors} from 'angular2/src/render/dom/view/proto_view_merger';
export function main() {
describe('ProtoViewMerger test', () => {
describe('mergeSelectors', () => {
it('should merge empty selectors', () => {
expect(mergeSelectors('', 'a')).toEqual('a');
expect(mergeSelectors('a', '')).toEqual('a');
expect(mergeSelectors('', '')).toEqual('');
});
it('should merge wildcard selectors', () => {
expect(mergeSelectors('*', 'a')).toEqual('a');
expect(mergeSelectors('a', '*')).toEqual('a');
expect(mergeSelectors('*', '*')).toEqual('*');
});
it('should merge 2 element selectors',
() => { expect(mergeSelectors('a', 'b')).toEqual('_not-matchable_'); });
it('should merge elements and non element selector', () => {
expect(mergeSelectors('a', '.b')).toEqual('a.b');
expect(mergeSelectors('.b', 'a')).toEqual('a.b');
});
it('should merge attributes', () => {
expect(mergeSelectors('[a]', '[b]')).toEqual('[a][b]');
expect(mergeSelectors('[a][b]', '[c][d]')).toEqual('[a][b][c][d]');
expect(mergeSelectors('[a=1]', '[b=2]')).toEqual('[a=1][b=2]');
});
it('should merge classes', () => {
expect(mergeSelectors('.a', '.b')).toEqual('.a.b');
expect(mergeSelectors('.a.b', '.c.d')).toEqual('.a.b.c.d');
});
});
});
}
@@ -30,7 +30,7 @@ export function main() {
binders = [];
}
var rootEl = DOM.createTemplate('<div></div>');
return DomProtoView.create(null, <Element>rootEl, [1], [], binders, null, null, null);
return DomProtoView.create(null, <Element>rootEl, [1], [], binders);
}
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }