perf(render): only create LightDom instances if the element has children

This commit is contained in:
Tobias Bosch 2015-06-10 15:54:10 -07:00
parent 4f27611ae6
commit ca09701343
5 changed files with 59 additions and 12 deletions

View File

@ -276,11 +276,15 @@ export class DomRenderer extends Renderer {
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
var binder = binders[binderIdx]; var binder = binders[binderIdx];
var element = boundElements[binderIdx]; var element = boundElements[binderIdx];
var domEl = element.element;
// lightDoms // lightDoms
var lightDom = null; var lightDom = null;
if (isPresent(binder.componentId)) { // Note: for the root element we can't use the binder.elementIsEmpty
lightDom = this._shadowDomStrategy.constructLightDom(view, element.element); // information as we don't use the element from the ProtoView
// but an element from the document.
if (isPresent(binder.componentId) && (!binder.elementIsEmpty || isPresent(inplaceElement))) {
lightDom = this._shadowDomStrategy.constructLightDom(view, domEl);
} }
element.lightDom = lightDom; element.lightDom = lightDom;
@ -294,7 +298,7 @@ export class DomRenderer extends Renderer {
// events // events
if (isPresent(binder.eventLocals) && isPresent(binder.localEvents)) { if (isPresent(binder.eventLocals) && isPresent(binder.localEvents)) {
for (var i = 0; i < binder.localEvents.length; i++) { for (var i = 0; i < binder.localEvents.length; i++) {
this._createEventListener(view, element.element, binderIdx, binder.localEvents[i].name, this._createEventListener(view, domEl, binderIdx, binder.localEvents[i].name,
binder.eventLocals); binder.eventLocals);
} }
} }

View File

@ -15,10 +15,11 @@ export class ElementBinder {
distanceToParent: number; distanceToParent: number;
propertySetters: Map<string, SetterFn>; propertySetters: Map<string, SetterFn>;
hostActions: Map<string, AST>; hostActions: Map<string, AST>;
elementIsEmpty: boolean;
constructor({textNodeIndices, contentTagSelector, nestedProtoView, componentId, eventLocals, constructor({textNodeIndices, contentTagSelector, nestedProtoView, componentId, eventLocals,
localEvents, globalEvents, hostActions, parentIndex, distanceToParent, localEvents, globalEvents, hostActions, parentIndex, distanceToParent,
propertySetters}: { propertySetters, elementIsEmpty}: {
contentTagSelector?: string, contentTagSelector?: string,
textNodeIndices?: List<number>, textNodeIndices?: List<number>,
nestedProtoView?: protoViewModule.DomProtoView, nestedProtoView?: protoViewModule.DomProtoView,
@ -29,7 +30,8 @@ export class ElementBinder {
parentIndex?: number, parentIndex?: number,
distanceToParent?: number, distanceToParent?: number,
propertySetters?: Map<string, SetterFn>, propertySetters?: Map<string, SetterFn>,
hostActions?: Map<string, AST> hostActions?: Map<string, AST>,
elementIsEmpty?: boolean
} = {}) { } = {}) {
this.textNodeIndices = textNodeIndices; this.textNodeIndices = textNodeIndices;
this.contentTagSelector = contentTagSelector; this.contentTagSelector = contentTagSelector;
@ -42,6 +44,7 @@ export class ElementBinder {
this.parentIndex = parentIndex; this.parentIndex = parentIndex;
this.distanceToParent = distanceToParent; this.distanceToParent = distanceToParent;
this.propertySetters = propertySetters; this.propertySetters = propertySetters;
this.elementIsEmpty = elementIsEmpty;
} }
} }

View File

@ -57,7 +57,8 @@ export class ProtoViewBuilder {
MapWrapper.forEach(dbb.hostPropertyBindings, (_, hostPropertyName) => { MapWrapper.forEach(dbb.hostPropertyBindings, (_, hostPropertyName) => {
MapWrapper.set(propertySetters, hostPropertyName, MapWrapper.set(propertySetters, hostPropertyName,
setterFactory.createSetter(ebb.element, isPresent(ebb.componentId), hostPropertyName)); setterFactory.createSetter(ebb.element, isPresent(ebb.componentId),
hostPropertyName));
}); });
ListWrapper.forEach(dbb.hostActions, (hostAction) => { ListWrapper.forEach(dbb.hostActions, (hostAction) => {
@ -73,7 +74,9 @@ export class ProtoViewBuilder {
}); });
MapWrapper.forEach(ebb.propertyBindings, (_, propertyName) => { MapWrapper.forEach(ebb.propertyBindings, (_, propertyName) => {
MapWrapper.set(propertySetters, propertyName, setterFactory.createSetter(ebb.element, isPresent(ebb.componentId), propertyName)); MapWrapper.set(
propertySetters, propertyName,
setterFactory.createSetter(ebb.element, isPresent(ebb.componentId), propertyName));
}); });
var nestedProtoView = var nestedProtoView =
@ -99,6 +102,7 @@ export class ProtoViewBuilder {
textBindings: ebb.textBindings, textBindings: ebb.textBindings,
readAttributes: ebb.readAttributes readAttributes: ebb.readAttributes
})); }));
var elementIsEmpty = this._isEmptyElement(ebb.element);
ListWrapper.push(renderElementBinders, new ElementBinder({ ListWrapper.push(renderElementBinders, new ElementBinder({
textNodeIndices: ebb.textBindingIndices, textNodeIndices: ebb.textBindingIndices,
contentTagSelector: ebb.contentTagSelector, contentTagSelector: ebb.contentTagSelector,
@ -112,7 +116,8 @@ export class ProtoViewBuilder {
localEvents: ebb.eventBuilder.buildLocalEvents(), localEvents: ebb.eventBuilder.buildLocalEvents(),
globalEvents: ebb.eventBuilder.buildGlobalEvents(), globalEvents: ebb.eventBuilder.buildGlobalEvents(),
hostActions: hostActions, hostActions: hostActions,
propertySetters: propertySetters propertySetters: propertySetters,
elementIsEmpty: elementIsEmpty
})); }));
}); });
return new api.ProtoViewDto({ return new api.ProtoViewDto({
@ -126,6 +131,18 @@ export class ProtoViewBuilder {
variableBindings: this.variableBindings variableBindings: this.variableBindings
}); });
} }
_isEmptyElement(el) {
var childNodes = DOM.childNodes(el);
for (var i = 0; i < childNodes.length; i++) {
var node = childNodes[i];
if ((DOM.isTextNode(node) && DOM.getText(node).trim().length > 0) ||
(DOM.isElementNode(node))) {
return false;
}
}
return true;
}
} }
export class ElementBinderBuilder { export class ElementBinderBuilder {

View File

@ -70,6 +70,25 @@ export function main() {
}); });
})); }));
it('should not create LightDom instances if the host element is empty',
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
tb.compileAll([
someComponent,
new ViewDefinition({
componentId: 'someComponent', template: '<some-comp> <!-- comment -->\n </some-comp>',
directives: [someComponent]
})
])
.then((protoViewDtos) => {
var rootView = tb.createRootView(protoViewDtos[0]);
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
expect(cmpView.rawView.proto.elementBinders[0].componentId).toBe('someComponent');
expect(cmpView.rawView.boundElements[0].lightDom).toBe(null);
async.done();
});
}));
it('should update text nodes', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { it('should update text nodes', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
tb.compileAll([ tb.compileAll([
someComponent, someComponent,

View File

@ -48,20 +48,24 @@ export function main() {
return new DomView(pv, [DOM.childNodes(root)[0]], [], boundElements); return new DomView(pv, [DOM.childNodes(root)[0]], [], boundElements);
} }
function createElementBinder(parentIndex:number = 0, distanceToParent:number = 1) {
return new ElementBinder({parentIndex: parentIndex, distanceToParent:distanceToParent, textNodeIndices:[]});
}
describe('getDirectParentElement', () => { describe('getDirectParentElement', () => {
it('should return the DomElement of the direct parent', () => { it('should return the DomElement of the direct parent', () => {
var pv = createProtoView( var pv = createProtoView(
[new ElementBinder(), new ElementBinder({parentIndex: 0, distanceToParent: 1})]); [createElementBinder(), createElementBinder(0, 1)]);
var view = createView(pv, 2); var view = createView(pv, 2);
expect(view.getDirectParentElement(1)).toBe(view.boundElements[0]); expect(view.getDirectParentElement(1)).toBe(view.boundElements[0]);
}); });
it('should return null if the direct parent is not bound', () => { it('should return null if the direct parent is not bound', () => {
var pv = createProtoView([ var pv = createProtoView([
new ElementBinder(), createElementBinder(),
new ElementBinder(), createElementBinder(),
new ElementBinder({parentIndex: 0, distanceToParent: 2}) createElementBinder(0,2)
]); ]);
var view = createView(pv, 3); var view = createView(pv, 3);
expect(view.getDirectParentElement(2)).toBe(null); expect(view.getDirectParentElement(2)).toBe(null);