Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 1c4e9e7

Browse files
mheverychirayuk
authored andcommitted
perf(view): increase view instantiation speed 40%
1) Stop using futures when the value is already cached 2) Remove adding nodes to fake parent during view instantiation Closes #1358
1 parent 31dd24a commit 1c4e9e7

File tree

9 files changed

+170
-112
lines changed

9 files changed

+170
-112
lines changed

lib/core_dom/directive.dart

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class NodeAttrs {
2222

2323
NodeAttrs(this.element);
2424

25-
operator [](String attrName) => element.attributes[attrName];
25+
operator [](String attrName) => element.getAttribute(attrName);
2626

2727
void operator []=(String attrName, String value) {
2828
if (_mustacheAttrs.containsKey(attrName)) {
@@ -31,7 +31,7 @@ class NodeAttrs {
3131
if (value == null) {
3232
element.attributes.remove(attrName);
3333
} else {
34-
element.attributes[attrName] = value;
34+
element.setAttribute(attrName, value);
3535
}
3636

3737
if (_observers != null && _observers.containsKey(attrName)) {
@@ -86,9 +86,18 @@ class NodeAttrs {
8686
* ShadowRoot is ready.
8787
*/
8888
class TemplateLoader {
89-
final async.Future<dom.Node> template;
89+
async.Future<dom.Node> _template;
90+
List<async.Future> _futures;
91+
final dom.Node _shadowRoot;
9092

91-
TemplateLoader(this.template);
93+
TemplateLoader(this._shadowRoot, this._futures);
94+
95+
async.Future<dom.Node> get template {
96+
if (_template == null) {
97+
_template = async.Future.wait(_futures).then((_) => _shadowRoot);
98+
}
99+
return _template;
100+
}
92101
}
93102

94103
class _MustacheAttr {

lib/core_dom/shadow_dom_component_factory.dart

Lines changed: 85 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract class BoundComponentFactory {
1111
List<Key> get callArgs;
1212
Function call(dom.Element element);
1313

14-
static async.Future<ViewFactory> _viewFuture(
14+
static async.Future<ViewFactory> _viewFactoryFuture(
1515
Component component, ViewCache viewCache, DirectiveMap directives) {
1616
if (component.template != null) {
1717
return new async.Future.value(viewCache.fromHtml(component.template, directives));
@@ -65,20 +65,27 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
6565
Component get _component => _ref.annotation as Component;
6666

6767
String _tag;
68-
async.Future<Iterable<dom.StyleElement>> _styleElementsFuture;
69-
async.Future<ViewFactory> _viewFuture;
68+
async.Future<List<dom.StyleElement>> _styleElementsFuture;
69+
List<dom.StyleElement> _styleElements;
70+
async.Future<ViewFactory> _shadowViewFactoryFuture;
71+
ViewFactory _shadowViewFactory;
7072

7173
BoundShadowDomComponentFactory(this._componentFactory, this._ref, this._directives) {
7274
_tag = _component.selector.toLowerCase();
73-
_styleElementsFuture = async.Future.wait(_component.cssUrls.map(_styleFuture));
75+
_styleElementsFuture = async.Future.wait(_component.cssUrls.map(_urlToStyle))
76+
..then((stylesElements) => _styleElements = stylesElements);
7477

75-
_viewFuture = BoundComponentFactory._viewFuture(
78+
_shadowViewFactoryFuture = BoundComponentFactory._viewFactoryFuture(
7679
_component,
80+
// TODO(misko): Why do we create a new one per Component. This kind of defeats the caching.
7781
new PlatformViewCache(_componentFactory.viewCache, _tag, _componentFactory.platform),
7882
_directives);
83+
if (_shadowViewFactoryFuture != null) {
84+
_shadowViewFactoryFuture.then((viewFactory) => _shadowViewFactory = viewFactory);
85+
}
7986
}
8087

81-
async.Future<dom.StyleElement> _styleFuture(cssUrl) {
88+
async.Future<dom.StyleElement> _urlToStyle(cssUrl) {
8289
Http http = _componentFactory.http;
8390
TemplateCache templateCache = _componentFactory.templateCache;
8491
WebPlatform platform = _componentFactory.platform;
@@ -107,7 +114,7 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
107114

108115
// If the css shim is required, it means that scoping does not
109116
// work, and adding the style to the head of the document is
110-
// preferrable.
117+
// preferable.
111118
if (platform.cssShimRequired) {
112119
dom.document.head.append(styleElement);
113120
return null;
@@ -126,47 +133,57 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
126133
EventHandler eventHandler) {
127134
var s = traceEnter(View_createComponent);
128135
try {
129-
var shadowDom = element.createShadowRoot()
136+
var shadowScope = scope.createChild(new HashMap()); // Isolate
137+
ComponentDirectiveInjector shadowInjector;
138+
dom.ShadowRoot shadowRoot = element.createShadowRoot();
139+
shadowRoot
130140
..applyAuthorStyles = _component.applyAuthorStyles
131141
..resetStyleInheritance = _component.resetStyleInheritance;
132142

133-
var shadowScope = scope.createChild(new HashMap()); // Isolate
143+
List<async.Future> futures = <async.Future>[];
144+
TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures);
145+
shadowInjector = new ShadowDomComponentDirectiveInjector(
146+
injector, injector.appInjector, shadowScope, templateLoader, shadowRoot);
147+
shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys,
148+
_ref.annotation.visibility);
149+
dom.Node firstViewNode = null;
150+
151+
// Load ngBase CSS
152+
if (_component.useNgBaseCss == true && baseCss.urls.isNotEmpty) {
153+
if (baseCss.styles == null) {
154+
futures.add(async.Future
155+
.wait(baseCss.urls.map(_urlToStyle))
156+
.then((List<dom.StyleElement> cssList) {
157+
baseCss.styles = cssList;
158+
_insertCss(cssList, shadowRoot, shadowRoot.firstChild);
159+
}));
160+
} else {
161+
_insertCss(baseCss.styles, shadowRoot, shadowRoot.firstChild);
162+
}
163+
}
134164

135-
async.Future<Iterable<dom.StyleElement>> cssFuture;
136-
if (_component.useNgBaseCss == true) {
137-
cssFuture = async.Future.wait([async.Future.wait(baseCss.urls.map(_styleFuture)), _styleElementsFuture]).then((twoLists) {
138-
assert(twoLists.length == 2);return []
139-
..addAll(twoLists[0])
140-
..addAll(twoLists[1]);
141-
});
142-
} else {
143-
cssFuture = _styleElementsFuture;
165+
if (_styleElementsFuture != null) {
166+
if (_styleElements == null) {
167+
futures.add(_styleElementsFuture .then((List<dom.StyleElement> styles) =>
168+
_insertCss(styles, shadowRoot, firstViewNode)));
169+
} else {
170+
_insertCss(_styleElements, shadowRoot);
171+
}
144172
}
145173

146-
ComponentDirectiveInjector shadowInjector;
147174

148-
TemplateLoader templateLoader = new TemplateLoader(cssFuture.then((Iterable<dom.StyleElement> cssList) {
149-
cssList.where((styleElement) => styleElement != null).forEach((styleElement) {
150-
shadowDom.append(styleElement.clone(true));
151-
});
152-
if (_viewFuture != null) {
153-
return _viewFuture.then((ViewFactory viewFactory) {
154-
if (shadowScope.isAttached) {
155-
shadowDom.nodes.addAll(viewFactory.call(shadowInjector.scope, shadowInjector).nodes);
156-
}
157-
return shadowDom;
158-
});
175+
if (_shadowViewFactoryFuture != null) {
176+
if (_shadowViewFactory == null) {
177+
futures.add(_shadowViewFactoryFuture.then((ViewFactory viewFactory) =>
178+
firstViewNode = _insertView(viewFactory, shadowRoot, shadowScope, shadowInjector)));
179+
} else {
180+
_insertView(_shadowViewFactory, shadowRoot, shadowScope, shadowInjector);
159181
}
160-
return shadowDom;
161-
}));
162-
163-
var probe;
164-
shadowInjector = new ShadowDomComponentDirectiveInjector(injector, injector.appInjector, shadowScope, templateLoader, shadowDom);
165-
shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
182+
}
166183

167184
if (_componentFactory.config.elementProbeEnabled) {
168-
probe = _componentFactory.expando[shadowDom] = shadowInjector.elementProbe;
169-
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowDom] = null);
185+
ElementProbe probe = _componentFactory.expando[shadowRoot] = shadowInjector.elementProbe;
186+
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowRoot] = null);
170187
}
171188

172189
var controller = shadowInjector.getByKey(_ref.typeKey);
@@ -180,6 +197,36 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
180197
}
181198
};
182199
}
200+
201+
_insertCss(List<dom.StyleElement> cssList,
202+
dom.ShadowRoot shadowRoot,
203+
[dom.Node insertBefore = null]) {
204+
var s = traceEnter(View_styles);
205+
for(int i = 0; i < cssList.length; i++) {
206+
var styleElement = cssList[i];
207+
if (styleElement != null) {
208+
shadowRoot.insertBefore(styleElement.clone(true), insertBefore);
209+
}
210+
}
211+
traceLeave(s);
212+
}
213+
214+
dom.Node _insertView(ViewFactory viewFactory,
215+
dom.ShadowRoot shadowRoot,
216+
Scope shadowScope,
217+
ShadowDomComponentDirectiveInjector shadowInjector) {
218+
dom.Node first = null;
219+
if (shadowScope.isAttached) {
220+
View shadowView = viewFactory.call(shadowScope, shadowInjector);
221+
List<dom.Node> shadowViewNodes = shadowView.nodes;
222+
for (var j = 0; j < shadowViewNodes.length; j++) {
223+
var node = shadowViewNodes[j];
224+
if (j == 0) first = node;
225+
shadowRoot.append(node);
226+
}
227+
}
228+
return first;
229+
}
183230
}
184231

185232
class _ComponentAssetKey {

lib/core_dom/transcluding_component_factory.dart

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,14 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory {
8787
final DirectiveMap _directives;
8888

8989
Component get _component => _ref.annotation as Component;
90-
async.Future<ViewFactory> _viewFuture;
90+
async.Future<ViewFactory> _viewFactoryFuture;
91+
ViewFactory _viewFactory;
9192

9293
BoundTranscludingComponentFactory(this._f, this._ref, this._directives) {
93-
_viewFuture = BoundComponentFactory._viewFuture(
94-
_component,
95-
_f.viewCache,
96-
_directives);
94+
_viewFactoryFuture = BoundComponentFactory._viewFactoryFuture(_component, _f.viewCache, _directives);
95+
if (_viewFactoryFuture != null) {
96+
_viewFactoryFuture.then((viewFactory) => _viewFactory = viewFactory);
97+
}
9798
}
9899

99100
List<Key> get callArgs => _CALL_ARGS;
@@ -110,52 +111,40 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory {
110111
ViewCache viewCache, Http http, TemplateCache templateCache,
111112
DirectiveMap directives, NgBaseCss baseCss, EventHandler eventHandler) {
112113

113-
DirectiveInjector childInjector;
114-
var childInjectorCompleter; // Used if the ViewFuture is available before the childInjector.
115-
116-
var component = _component;
114+
List<async.Future> futures = [];
117115
var contentPort = new ContentPort(element);
118-
119-
// Append the component's template as children
120-
var elementFuture;
121-
122-
if (_viewFuture != null) {
123-
elementFuture = _viewFuture.then((ViewFactory viewFactory) {
124-
contentPort.pullNodes();
125-
if (childInjector != null) {
126-
element.nodes.addAll(
127-
viewFactory.call(childInjector.scope, childInjector).nodes);
128-
return element;
129-
} else {
130-
childInjectorCompleter = new async.Completer();
131-
return childInjectorCompleter.future.then((childInjector) {
132-
element.nodes.addAll(
133-
viewFactory.call(childInjector.scope, childInjector).nodes);
134-
return element;
135-
});
136-
}
137-
});
138-
} else {
139-
elementFuture = new async.Future.microtask(() => contentPort.pullNodes());
140-
}
141-
TemplateLoader templateLoader = new TemplateLoader(elementFuture);
142-
116+
TemplateLoader templateLoader = new TemplateLoader(element, futures);
143117
Scope shadowScope = scope.createChild(new HashMap());
144-
145-
childInjector = new ShadowlessComponentDirectiveInjector(injector, injector.appInjector,
146-
eventHandler, shadowScope, templateLoader, new ShadowlessShadowRoot(element),
147-
contentPort);
118+
DirectiveInjector childInjector = new ShadowlessComponentDirectiveInjector(
119+
injector, injector.appInjector, eventHandler, shadowScope, templateLoader,
120+
new ShadowlessShadowRoot(element), contentPort);
148121
childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
149122

150-
if (childInjectorCompleter != null) {
151-
childInjectorCompleter.complete(childInjector);
152-
}
153-
154123
var controller = childInjector.getByKey(_ref.typeKey);
155124
shadowScope.context[component.publishAs] = controller;
156125
if (controller is ScopeAware) controller.scope = shadowScope;
157126
BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
127+
128+
if (_viewFactoryFuture != null && _viewFactory == null) {
129+
futures.add(_viewFactoryFuture.then((ViewFactory viewFactory) =>
130+
_insert(viewFactory, element, childInjector, contentPort)));
131+
} else {
132+
scope.rootScope.runAsync(() {
133+
_insert(_viewFactory, element, childInjector, contentPort);
134+
});
135+
}
158136
return controller;
159137
};
160138
}
139+
140+
_insert(ViewFactory viewFactory, dom.Element element, DirectiveInjector childInjector,
141+
ContentPort contentPort) {
142+
contentPort.pullNodes();
143+
if (viewFactory != null) {
144+
var viewNodes = viewFactory.call(childInjector.scope, childInjector).nodes;
145+
for(var i = 0; i < viewNodes.length; i++) {
146+
element.append(viewNodes[i]);
147+
}
148+
}
149+
}
161150
}

lib/core_dom/view_factory.dart

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ class ViewFactory implements Function {
8989
}
9090
elementInjectors[elementBinderIndex] = elementInjector;
9191

92-
if (tagged.textBinders != null) {
93-
for (var k = 0; k < tagged.textBinders.length; k++) {
94-
TaggedTextBinder taggedText = tagged.textBinders[k];
95-
var childNode = boundNode.childNodes[taggedText.offsetIndex];
92+
var textBinders = tagged.textBinders;
93+
if (textBinders != null && textBinders.length > 0) {
94+
var childNodes = boundNode.childNodes;
95+
for (var k = 0; k < textBinders.length; k++) {
96+
TaggedTextBinder taggedText = textBinders[k];
97+
var childNode = childNodes[taggedText.offsetIndex];
9698
taggedText.binder.bind(view, scope, elementInjector, childNode, eventHandler, animate);
9799
}
98100
}
@@ -108,15 +110,6 @@ class ViewFactory implements Function {
108110
dom.Node node = nodeList[i];
109111
NodeLinkingInfo linkingInfo = nodeLinkingInfos[i];
110112

111-
// if node isn't attached to the DOM, create a parent for it.
112-
var parentNode = node.parentNode;
113-
var fakeParent = false;
114-
if (parentNode == null) {
115-
fakeParent = true;
116-
parentNode = new dom.DivElement();
117-
parentNode.append(node);
118-
}
119-
120113
if (linkingInfo.isElement) {
121114
if (linkingInfo.containsNgBinding) {
122115
var tagged = elementBinders[elementBinderIndex];
@@ -142,11 +135,6 @@ class ViewFactory implements Function {
142135
}
143136
elementBinderIndex++;
144137
}
145-
146-
if (fakeParent) {
147-
// extract the node from the parentNode.
148-
nodeList[i] = parentNode.nodes[0];
149-
}
150138
}
151139
return view;
152140
}

lib/core_dom/web_platform.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class WebPlatform {
4242
//
4343
// TODO Remove the try-catch once https://github.com/angular/angular.dart/issues/1189 is fixed.
4444
try {
45-
root.querySelectorAll("*").forEach((n) => n.attributes[selector] = "");
45+
root.querySelectorAll("*").forEach((dom.Element n) => n.setAttribute(selector, ""));
4646
} catch (e, s) {
4747
print("WARNING: Failed to set up Shadow DOM shim for $selector.\n$e\n$s");
4848
}
@@ -69,6 +69,7 @@ class PlatformViewCache implements ViewCache {
6969
if (selector != null && selector != "" && platform.shadowDomShimRequired) {
7070
// By adding a comment with the tag name we ensure the template html is unique per selector
7171
// name when used as a key in the view factory cache.
72+
//TODO(misko): This will always be miss, since we never put it in cache under such key.
7273
viewFactory = viewFactoryCache.get("<!-- Shimmed template for: <$selector> -->$html");
7374
} else {
7475
viewFactory = viewFactoryCache.get(html);

lib/directive/ng_base_css.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ part of angular.directive;
1212
selector: '[ng-base-css]',
1313
visibility: Visibility.CHILDREN)
1414
class NgBaseCss {
15+
List<dom.StyleElement> styles;
1516
List<String> _urls = const [];
1617

1718
@NgAttr('ng-base-css')
18-
set urls(v) => _urls = v is List ? v : [v];
19+
set urls(v) {
20+
_urls = v is List ? v : [v];
21+
styles = null;
22+
}
1923

2024
List<String> get urls => _urls;
2125
}

0 commit comments

Comments
 (0)