From eac36d1d26a283560742b3555886f5db99ee9c65 Mon Sep 17 00:00:00 2001 From: James deBoer Date: Tue, 1 Jul 2014 11:29:55 -0700 Subject: [PATCH] perf(view factory): 14% Precompute linking information for nodes The Default TreeComponent benchmark dropped from 572ms to 501ms Closes #1194 Closes #1196 --- lib/core_dom/tagging_view_factory.dart | 69 +++++++++++++++++++++----- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/lib/core_dom/tagging_view_factory.dart b/lib/core_dom/tagging_view_factory.dart index 00123aa93..d62fa1c21 100644 --- a/lib/core_dom/tagging_view_factory.dart +++ b/lib/core_dom/tagging_view_factory.dart @@ -1,11 +1,55 @@ part of angular.core.dom_internal; +class NodeLinkingInfo { + /** + * True if the Node has a 'ng-binding' class. + */ + final bool containsNgBinding; + + /** + * True if the Node is a [dom.Element], otherwise it is a Text or Comment node. + * No other nodeTypes are allowed. + */ + final bool isElement; + + /** + * If true, some child has a 'ng-binding' class and the ViewFactory must search + * for these children. + */ + final bool ngBindingChildren; + + NodeLinkingInfo(this.containsNgBinding, this.isElement, this.ngBindingChildren); +} + +computeNodeLinkingInfos(List nodeList) { + List list = new List(nodeList.length); + + for (int i = 0; i < nodeList.length; i++) { + dom.Node node = nodeList[i]; + + assert(node.nodeType == dom.Node.ELEMENT_NODE || + node.nodeType == dom.Node.TEXT_NODE || + node.nodeType == dom.Node.COMMENT_NODE); + + bool isElement = node.nodeType == dom.Node.ELEMENT_NODE; + + list[i] = new NodeLinkingInfo( + isElement && (node as dom.Element).classes.contains('ng-binding'), + isElement, + isElement && (node as dom.Element).querySelectorAll('.ng-binding').length > 0); + } + return list; +} + class TaggingViewFactory implements ViewFactory { final List elementBinders; final List templateNodes; + final List nodeLinkingInfos; final Profiler _perf; - TaggingViewFactory(this.templateNodes, this.elementBinders, this._perf); + TaggingViewFactory(templateNodes, this.elementBinders, this._perf) : + nodeLinkingInfos = computeNodeLinkingInfos(templateNodes), + this.templateNodes = templateNodes; BoundViewFactory bind(Injector injector) => new BoundViewFactory(this, injector); @@ -51,7 +95,8 @@ class TaggingViewFactory implements ViewFactory { var elementBinderIndex = 0; for (int i = 0; i < nodeList.length; i++) { - var node = nodeList[i]; + dom.Node node = nodeList[i]; + NodeLinkingInfo linkingInfo = nodeLinkingInfos[i]; // if node isn't attached to the DOM, create a parent for it. var parentNode = node.parentNode; @@ -62,29 +107,27 @@ class TaggingViewFactory implements ViewFactory { parentNode.append(node); } - if (node.nodeType == dom.Node.ELEMENT_NODE) { - var elts = node.querySelectorAll('.ng-binding'); - // querySelectorAll doesn't return the node itself - if (node.classes.contains('ng-binding')) { + if (linkingInfo.isElement) { + if (linkingInfo.containsNgBinding) { var tagged = elementBinders[elementBinderIndex]; _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, node); elementBinderIndex++; } - for (int j = 0; j < elts.length; j++, elementBinderIndex++) { - TaggedElementBinder tagged = elementBinders[elementBinderIndex]; - _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, elts[j]); + if (linkingInfo.ngBindingChildren) { + var elts = (node as dom.Element).querySelectorAll('.ng-binding'); + for (int j = 0; j < elts.length; j++, elementBinderIndex++) { + TaggedElementBinder tagged = elementBinders[elementBinderIndex]; + _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, elts[j]); + } } - } else if (node.nodeType == dom.Node.TEXT_NODE || - node.nodeType == dom.Node.COMMENT_NODE) { + } else { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; assert(tagged.binder != null || tagged.isTopLevel); if (tagged.binder != null) { _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, node); } elementBinderIndex++; - } else { - throw "nodeType sadness ${node.nodeType}}"; } if (fakeParent) {