Skip to content

Commit b07b473

Browse files
mheveryvsavkin
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 dart-archive#1358
1 parent 3983b5f commit b07b473

10 files changed

+169
-115
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: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ 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
TypeToUriMapper uriMapper, ResourceUrlResolver resourceResolver, Type type) {
17-
1817
if (component.template != null) {
1918
// TODO(chirayu): Replace this line with
2019
// var baseUri = uriMapper.uriForType(type);
@@ -77,73 +76,93 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
7776
Component get _component => _ref.annotation as Component;
7877

7978
String _tag;
80-
async.Future<Iterable<dom.StyleElement>> _styleElementsFuture;
81-
async.Future<ViewFactory> _viewFuture;
79+
async.Future<List<dom.StyleElement>> _styleElementsFuture;
80+
List<dom.StyleElement> _styleElements;
81+
async.Future<ViewFactory> _shadowViewFactoryFuture;
82+
ViewFactory _shadowViewFactory;
8283

8384
BoundShadowDomComponentFactory(this._componentFactory, this._ref,
8485
DirectiveMap directives, this._injector) {
8586
_tag = _ref.annotation.selector.toLowerCase();
86-
_styleElementsFuture = _componentFactory.cssLoader(_tag, _component.cssUrls, type: _ref.type);
87+
_styleElementsFuture = _componentFactory.cssLoader(_tag, _component.cssUrls, type: _ref.type)
88+
.then((styleElements) => _styleElements = styleElements);
8789

8890
final viewCache = new ShimmingViewCache(_componentFactory.viewCache,
8991
_tag, _componentFactory.platformShim);
90-
_viewFuture = BoundComponentFactory._viewFuture(_component, viewCache, directives,
91-
_componentFactory.uriMapper, _componentFactory.resourceResolver, _ref.type);
92+
93+
_shadowViewFactoryFuture = BoundComponentFactory._viewFactoryFuture(_component,
94+
viewCache, directives, _componentFactory.uriMapper,
95+
_componentFactory.resourceResolver, _ref.type);
96+
97+
if (_shadowViewFactoryFuture != null) {
98+
_shadowViewFactoryFuture.then((viewFactory) => _shadowViewFactory = viewFactory);
99+
}
92100
}
93101

94102
List<Key> get callArgs => _CALL_ARGS;
95103
static final _CALL_ARGS = [DIRECTIVE_INJECTOR_KEY, SCOPE_KEY, VIEW_KEY, NG_BASE_CSS_KEY,
96104
SHADOW_BOUNDARY_KEY];
105+
97106
Function call(dom.Element element) {
98107
return (DirectiveInjector injector, Scope scope, View view, NgBaseCss baseCss,
99108
ShadowBoundary parentShadowBoundary) {
100109
var s = traceEnter(View_createComponent);
101110
try {
102-
var shadowDom = element.createShadowRoot();
111+
var shadowRoot = element.createShadowRoot();
103112

104113
var shadowBoundary;
105114
if (_componentFactory.platformShim.shimRequired) {
106115
shadowBoundary = parentShadowBoundary;
107116
} else {
108-
shadowBoundary = new ShadowRootBoundary(shadowDom);
117+
shadowBoundary = new ShadowRootBoundary(shadowRoot);
109118
}
110119

111-
//_styleFuture(cssUrl, resolveUri: false)
112120
var shadowScope = scope.createChild(new HashMap()); // Isolate
113-
ComponentDirectiveInjector shadowInjector;
114-
115-
final baseUrls = (_component.useNgBaseCss) ? baseCss.urls : [];
116-
final baseUrlsFuture = _componentFactory.cssLoader(_tag, baseUrls);
117-
final cssFuture = mergeFutures(baseUrlsFuture, _styleElementsFuture);
118-
119-
async.Future<dom.Node> initShadowDom(_) {
120-
if (_viewFuture == null) return new async.Future.value(shadowDom);
121-
return _viewFuture.then((ViewFactory viewFactory) {
122-
if (shadowScope.isAttached) {
123-
shadowDom.nodes.addAll(
124-
viewFactory.call(shadowInjector.scope, shadowInjector).nodes);
125-
}
126-
return shadowDom;
127-
});
128-
}
129-
130-
TemplateLoader templateLoader = new TemplateLoader(
131-
cssFuture.then(shadowBoundary.insertStyleElements).then(initShadowDom));
121+
List<async.Future> futures = <async.Future>[];
122+
TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures);
132123

133124
var probe;
134125
var eventHandler = new ShadowRootEventHandler(
135-
shadowDom, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY));
136-
shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope,
137-
templateLoader, shadowDom, null, view, shadowBoundary);
126+
shadowRoot, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY));
127+
final shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope,
128+
templateLoader, shadowRoot, null, view, shadowBoundary);
129+
130+
if (_component.useNgBaseCss && baseCss.urls.isNotEmpty) {
131+
if (baseCss.styles == null) {
132+
final f = _componentFactory.cssLoader(_tag, baseCss.urls).then((cssList) {
133+
baseCss.styles = cssList;
134+
shadowBoundary.insertStyleElements(cssList);
135+
});
136+
futures.add(f);
137+
} else {
138+
shadowBoundary.insertStyleElements(baseCss.styles);
139+
}
140+
}
138141

142+
if (_styleElementsFuture != null) {
143+
if (_styleElements == null) {
144+
final f = _styleElementsFuture.then(shadowBoundary.insertStyleElements);
145+
futures.add(f);
146+
} else {
147+
shadowBoundary.insertStyleElements(_styleElements);
148+
}
149+
}
139150

140-
shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
151+
if (_shadowViewFactoryFuture != null) {
152+
if (_shadowViewFactory == null) {
153+
futures.add(_shadowViewFactoryFuture.then((ViewFactory viewFactory) =>
154+
_insertView(viewFactory, shadowRoot, shadowScope, shadowInjector)));
155+
} else {
156+
_insertView(_shadowViewFactory, shadowRoot, shadowScope, shadowInjector);
157+
}
158+
}
141159

142160
if (_componentFactory.config.elementProbeEnabled) {
143-
probe = _componentFactory.expando[shadowDom] = shadowInjector.elementProbe;
144-
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowDom] = null);
161+
ElementProbe probe = _componentFactory.expando[shadowRoot] = shadowInjector.elementProbe;
162+
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowRoot] = null);
145163
}
146164

165+
shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
147166
var controller = shadowInjector.getByKey(_ref.typeKey);
148167
BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
149168
shadowScope.context[_component.publishAs] = controller;
@@ -154,6 +173,16 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
154173
}
155174
};
156175
}
176+
177+
dom.Node _insertView(ViewFactory viewFactory,
178+
dom.ShadowRoot shadowRoot,
179+
Scope shadowScope,
180+
ComponentDirectiveInjector shadowInjector) {
181+
if (shadowScope.isAttached) {
182+
shadowRoot.nodes.addAll(
183+
viewFactory.call(shadowInjector.scope, shadowInjector).nodes);
184+
}
185+
}
157186
}
158187

159188
@Injectable()

lib/core_dom/transcluding_component_factory.dart

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,88 +34,98 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory {
3434

3535
String _tag;
3636
async.Future<Iterable<dom.StyleElement>> _styleElementsFuture;
37+
List<dom.StyleElement> _styleElements;
3738

3839
Component get _component => _ref.annotation as Component;
39-
async.Future<ViewFactory> _viewFuture;
40+
async.Future<ViewFactory> _viewFactoryFuture;
41+
ViewFactory _viewFactory;
4042

4143
BoundTranscludingComponentFactory(this._f, this._ref, this._directives, this._injector) {
42-
_viewFuture = BoundComponentFactory._viewFuture(
44+
_tag = _ref.annotation.selector.toLowerCase();
45+
_styleElementsFuture = _f.cssLoader(_tag, _component.cssUrls, type: _ref.type)
46+
.then((styleElements) => _styleElements = styleElements);
47+
48+
final viewCache = new ShimmingViewCache(_f.viewCache, _tag, _f.platformShim);
49+
50+
_viewFactoryFuture = BoundComponentFactory._viewFactoryFuture(
4351
_component,
44-
_f.viewCache,
52+
viewCache,
4553
_directives,
4654
_f.uriMapper,
4755
_f.resourceResolver,
4856
_ref.type);
49-
50-
_tag = _ref.annotation.selector.toLowerCase();
51-
_styleElementsFuture = _f.cssLoader(_tag, _component.cssUrls, type: _ref.type);
5257

53-
final viewCache = new ShimmingViewCache(_f.viewCache, _tag, _f.platformShim);
54-
_viewFuture = BoundComponentFactory._viewFuture(_component, viewCache, _directives,
55-
_f.uriMapper, _f.resourceResolver, _ref.type);
58+
if (_viewFactoryFuture != null) {
59+
_viewFactoryFuture.then((viewFactory) => _viewFactory = viewFactory);
60+
}
5661
}
5762

5863
List<Key> get callArgs => _CALL_ARGS;
5964
static var _CALL_ARGS = [ DIRECTIVE_INJECTOR_KEY, SCOPE_KEY, VIEW_KEY,
6065
VIEW_CACHE_KEY, HTTP_KEY, TEMPLATE_CACHE_KEY,
6166
DIRECTIVE_MAP_KEY, NG_BASE_CSS_KEY, EVENT_HANDLER_KEY,
6267
SHADOW_BOUNDARY_KEY];
68+
6369
Function call(dom.Node node) {
6470
var element = node as dom.Element;
6571
return (DirectiveInjector injector, Scope scope, View view,
6672
ViewCache viewCache, Http http, TemplateCache templateCache,
6773
DirectiveMap directives, NgBaseCss baseCss, EventHandler eventHandler,
6874
ShadowBoundary shadowBoundary) {
6975

70-
DirectiveInjector childInjector;
71-
var childInjectorCompleter; // Used if the ViewFuture is available before the childInjector.
72-
73-
var component = _component;
7476
final shadowRoot = new EmulatedShadowRoot(element);
75-
var lightDom = new LightDom(element, scope)..pullNodes();
77+
final lightDom = new LightDom(element, scope)..pullNodes();
7678

77-
final baseUrls = (_component.useNgBaseCss) ? baseCss.urls : [];
78-
final baseUrlsFuture = _f.cssLoader(_tag, baseUrls);
79-
final cssFuture = mergeFutures(baseUrlsFuture, _styleElementsFuture);
79+
final shadowScope = scope.createChild(new HashMap());
80+
List<async.Future> futures = <async.Future>[];
81+
TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures);
8082

81-
initShadowDom(_) {
82-
if (_viewFuture != null) {
83-
return _viewFuture.then((ViewFactory viewFactory) {
84-
lightDom.clearComponentElement();
85-
if (childInjector != null) {
86-
lightDom.shadowDomView = viewFactory.call(childInjector.scope, childInjector);
87-
return shadowRoot;
88-
} else {
89-
childInjectorCompleter = new async.Completer();
90-
return childInjectorCompleter.future.then((childInjector) {
91-
lightDom.shadowDomView = viewFactory.call(childInjector.scope, childInjector);
92-
return shadowRoot;
93-
});
94-
}
83+
final childInjector = new ComponentDirectiveInjector(injector, this._injector,
84+
eventHandler, shadowScope, templateLoader, shadowRoot, lightDom, view);
85+
86+
if (_component.useNgBaseCss && baseCss.urls.isNotEmpty) {
87+
if (baseCss.styles == null) {
88+
final f = _f.cssLoader(_tag, baseCss.urls).then((cssList) {
89+
baseCss.styles = cssList;
90+
shadowBoundary.insertStyleElements(cssList);
9591
});
92+
futures.add(f);
9693
} else {
97-
return new async.Future.microtask(lightDom.clearComponentElement);
94+
shadowBoundary.insertStyleElements(baseCss.styles);
9895
}
9996
}
10097

101-
TemplateLoader templateLoader = new TemplateLoader(
102-
cssFuture.then(shadowBoundary.insertStyleElements).then(initShadowDom));
103-
104-
Scope shadowScope = scope.createChild(new HashMap());
105-
106-
childInjector = new ComponentDirectiveInjector(injector, this._injector,
107-
eventHandler, shadowScope, templateLoader, shadowRoot, lightDom, view);
108-
109-
childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
98+
if (_styleElementsFuture != null) {
99+
if (_styleElements == null) {
100+
final f = _styleElementsFuture.then(shadowBoundary.insertStyleElements);
101+
futures.add(f);
102+
} else {
103+
shadowBoundary.insertStyleElements(_styleElements);
104+
}
105+
}
110106

111-
if (childInjectorCompleter != null) {
112-
childInjectorCompleter.complete(childInjector);
107+
if (_viewFactoryFuture != null) {
108+
if (_viewFactory == null) {
109+
final f = _viewFactoryFuture.then((ViewFactory viewFactory) {
110+
lightDom.clearComponentElement();
111+
lightDom.shadowDomView = viewFactory.call(childInjector.scope, childInjector);
112+
});
113+
futures.add(f);
114+
} else {
115+
final f = new Future.microtask(() {
116+
lightDom.clearComponentElement();
117+
lightDom.shadowDomView = _viewFactory.call(childInjector.scope, childInjector);
118+
});
119+
futures.add(f);
120+
}
113121
}
114122

123+
childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
115124
var controller = childInjector.getByKey(_ref.typeKey);
116-
shadowScope.context[component.publishAs] = controller;
125+
shadowScope.context[_component.publishAs] = controller;
117126
BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
127+
118128
return controller;
119129
};
120130
}
121-
}
131+
}

lib/core_dom/view_factory.dart

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

87-
if (tagged.textBinders != null) {
88-
for (var k = 0; k < tagged.textBinders.length; k++) {
89-
TaggedTextBinder taggedText = tagged.textBinders[k];
90-
var childNode = boundNode.childNodes[taggedText.offsetIndex];
87+
var textBinders = tagged.textBinders;
88+
if (textBinders != null && textBinders.length > 0) {
89+
var childNodes = boundNode.childNodes;
90+
for (var k = 0; k < textBinders.length; k++) {
91+
TaggedTextBinder taggedText = textBinders[k];
92+
var childNode = childNodes[taggedText.offsetIndex];
9193
taggedText.binder.bind(view, scope, elementInjector, childNode);
9294
}
9395
}
@@ -102,15 +104,6 @@ class ViewFactory implements Function {
102104
dom.Node node = nodeList[i];
103105
NodeLinkingInfo linkingInfo = nodeLinkingInfos[i];
104106

105-
// if node isn't attached to the DOM, create a parent for it.
106-
var parentNode = node.parentNode;
107-
var fakeParent = false;
108-
if (parentNode == null) {
109-
fakeParent = true;
110-
parentNode = new dom.DivElement();
111-
parentNode.append(node);
112-
}
113-
114107
if (linkingInfo.isElement) {
115108
if (linkingInfo.containsNgBinding) {
116109
var tagged = elementBinders[elementBinderIndex];
@@ -136,11 +129,6 @@ class ViewFactory implements Function {
136129
}
137130
elementBinderIndex++;
138131
}
139-
140-
if (fakeParent) {
141-
// extract the node from the parentNode.
142-
nodeList[i] = parentNode.nodes[0];
143-
}
144132
}
145133
return view;
146134
}

0 commit comments

Comments
 (0)