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

Commit

Permalink
perf(view): increase view instantiation speed 40%
Browse files Browse the repository at this point in the history
1) Stop using futures when the value is already cached
2) Remove adding nodes to fake parent during view instantiation

Closes #1358
  • Loading branch information
mhevery authored and rkirov committed Sep 26, 2014
1 parent 97410f7 commit 00960bb
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 184 deletions.
17 changes: 13 additions & 4 deletions lib/core_dom/directive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class NodeAttrs {

NodeAttrs(this.element);

operator [](String attrName) => element.attributes[attrName];
operator [](String attrName) => element.getAttribute(attrName);

void operator []=(String attrName, String value) {
if (_mustacheAttrs.containsKey(attrName)) {
Expand All @@ -31,7 +31,7 @@ class NodeAttrs {
if (value == null) {
element.attributes.remove(attrName);
} else {
element.attributes[attrName] = value;
element.setAttribute(attrName, value);
}

if (_observers != null && _observers.containsKey(attrName)) {
Expand Down Expand Up @@ -86,9 +86,18 @@ class NodeAttrs {
* ShadowRoot is ready.
*/
class TemplateLoader {
final async.Future<dom.Node> template;
async.Future<dom.Node> _template;
List<async.Future> _futures;
final dom.Node _shadowRoot;

TemplateLoader(this.template);
TemplateLoader(this._shadowRoot, this._futures);

async.Future<dom.Node> get template {
if (_template == null) {
_template = async.Future.wait(_futures).then((_) => _shadowRoot);
}
return _template;
}
}

class _MustacheAttr {
Expand Down
59 changes: 29 additions & 30 deletions lib/core_dom/shadow_boundary.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,29 @@ part of angular.core.dom_internal;
*/
abstract class ShadowBoundary {
Set<dom.StyleElement> _insertedStyles;
final dom.Node root;
dom.StyleElement _lastStyleElement;

ShadowBoundary(this.root);

void insertStyleElements(List<dom.StyleElement> elements);
void insertStyleElements(List<dom.StyleElement> elements, {bool prepend: false}) {
if (elements.isEmpty) return;

final newStyles = _newStyles(elements);
final cloned = newStyles.map((el) => el.clone(true));

cloned.forEach((style) {
if (_lastStyleElement != null && !prepend) {
_lastStyleElement = root.insertBefore(style, _lastStyleElement.nextNode);
} else if (root.hasChildNodes()) {
_lastStyleElement = root.insertBefore(style, root.firstChild);
} else {
_lastStyleElement = root.append(style);
}
});

_addInsertedStyles(newStyles);
}

Iterable<dom.StyleElement> _newStyles(Iterable<dom.StyleElement> elements) {
if (_insertedStyles == null) return elements;
Expand All @@ -23,37 +44,15 @@ abstract class ShadowBoundary {

@Injectable()
class DefaultShadowBoundary extends ShadowBoundary {
void insertStyleElements(List<dom.StyleElement> elements) {
final newStyles = _newStyles(elements);
final cloned = newStyles.map((el) => el.clone(true));
dom.document.head.nodes.addAll(cloned);
_addInsertedStyles(newStyles);
}
DefaultShadowBoundary()
: super(dom.document.head);

DefaultShadowBoundary.custom(dom.Node node)
: super(node);
}

@Injectable()
class ShadowRootBoundary extends ShadowBoundary {
final dom.ShadowRoot shadowRoot;
dom.StyleElement _lastStyleElement;

ShadowRootBoundary(this.shadowRoot);

void insertStyleElements(List<dom.StyleElement> elements) {
if (elements.isEmpty) return;

final newStyles = _newStyles(elements);
final cloned = newStyles.map((el) => el.clone(true));

cloned.forEach((style) {
if (_lastStyleElement != null) {
_lastStyleElement = shadowRoot.insertBefore(style, _lastStyleElement.nextNode);
} else if (shadowRoot.hasChildNodes()) {
_lastStyleElement = shadowRoot.insertBefore(style, shadowRoot.firstChild);
} else {
_lastStyleElement = shadowRoot.append(style);
}
});

_addInsertedStyles(newStyles);
}
ShadowRootBoundary(dom.ShadowRoot shadowRoot)
: super(shadowRoot);
}
103 changes: 68 additions & 35 deletions lib/core_dom/shadow_dom_component_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ abstract class BoundComponentFactory {
List<Key> get callArgs;
Function call(dom.Element element);

static async.Future<ViewFactory> _viewFuture(
static async.Future<ViewFactory> _viewFactoryFuture(
Component component, ViewCache viewCache, DirectiveMap directives,
TypeToUriMapper uriMapper, ResourceUrlResolver resourceResolver, Type type) {

if (component.template != null) {
// TODO(chirayu): Replace this line with
// var baseUri = uriMapper.uriForType(type);
Expand Down Expand Up @@ -77,71 +76,95 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
Component get _component => _ref.annotation as Component;

String _tag;
async.Future<Iterable<dom.StyleElement>> _styleElementsFuture;
async.Future<ViewFactory> _viewFuture;
async.Future<List<dom.StyleElement>> _styleElementsFuture;
List<dom.StyleElement> _styleElements;
async.Future<ViewFactory> _shadowViewFactoryFuture;
ViewFactory _shadowViewFactory;

BoundShadowDomComponentFactory(this._componentFactory, this._ref,
DirectiveMap directives, this._injector) {
_tag = _ref.annotation.selector.toLowerCase();
_styleElementsFuture = _componentFactory.cssLoader(_tag, _component.cssUrls, type: _ref.type);
_styleElementsFuture = _componentFactory.cssLoader(_tag, _component.cssUrls, type: _ref.type)
.then((styleElements) => _styleElements = styleElements);

final viewCache = new ShimmingViewCache(_componentFactory.viewCache,
_tag, _componentFactory.platformShim);
_viewFuture = BoundComponentFactory._viewFuture(_component, viewCache, directives,
_componentFactory.uriMapper, _componentFactory.resourceResolver, _ref.type);

_shadowViewFactoryFuture = BoundComponentFactory._viewFactoryFuture(_component,
viewCache, directives, _componentFactory.uriMapper,
_componentFactory.resourceResolver, _ref.type);

if (_shadowViewFactoryFuture != null) {
_shadowViewFactoryFuture.then((viewFactory) => _shadowViewFactory = viewFactory);
}
}

List<Key> get callArgs => _CALL_ARGS;
static final _CALL_ARGS = [DIRECTIVE_INJECTOR_KEY, SCOPE_KEY, VIEW_KEY, NG_BASE_CSS_KEY,
SHADOW_BOUNDARY_KEY];

Function call(dom.Element element) {
return (DirectiveInjector injector, Scope scope, View view, NgBaseCss baseCss,
ShadowBoundary parentShadowBoundary) {
var s = traceEnter(View_createComponent);
try {
var shadowDom = element.createShadowRoot();
var shadowRoot = element.createShadowRoot();

var shadowBoundary;
if (_componentFactory.platformShim.shimRequired) {
shadowBoundary = parentShadowBoundary;
} else {
shadowBoundary = new ShadowRootBoundary(shadowDom);
shadowBoundary = new ShadowRootBoundary(shadowRoot);
}

//_styleFuture(cssUrl, resolveUri: false)
var shadowScope = scope.createChild(new HashMap()); // Isolate
ComponentDirectiveInjector shadowInjector;

final baseUrls = (_component.useNgBaseCss) ? baseCss.urls : [];
final baseUrlsFuture = _componentFactory.cssLoader(_tag, baseUrls);
final cssFuture = mergeFutures(baseUrlsFuture, _styleElementsFuture);

async.Future<dom.Node> initShadowDom(_) {
if (_viewFuture == null) return new async.Future.value(shadowDom);
return _viewFuture.then((ViewFactory viewFactory) {
if (shadowScope.isAttached) {
shadowDom.nodes.addAll(
viewFactory.call(shadowInjector.scope, shadowInjector).nodes);
}
return shadowDom;
});
}

TemplateLoader templateLoader = new TemplateLoader(
cssFuture.then(shadowBoundary.insertStyleElements).then(initShadowDom));
List<async.Future> futures = <async.Future>[];
TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures);

var probe;
var eventHandler = new ShadowRootEventHandler(
shadowDom, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY));
shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope,
templateLoader, shadowDom, null, view, shadowBoundary);
shadowRoot, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY));
final shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope,
templateLoader, shadowRoot, null, view, shadowBoundary);
shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);

if (_component.useNgBaseCss && baseCss.urls.isNotEmpty) {
if (baseCss.styles == null) {
final f = _componentFactory.cssLoader(_tag, baseCss.urls).then((cssList) {
baseCss.styles = cssList;
shadowBoundary.insertStyleElements(cssList, prepend: true);
});
futures.add(f);
} else {
shadowBoundary.insertStyleElements(baseCss.styles, prepend: true);
}
}

shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
if (_styleElementsFuture != null) {
if (_styleElements == null) {
final f = _styleElementsFuture.then(shadowBoundary.insertStyleElements);
futures.add(f);
} else {
shadowBoundary.insertStyleElements(_styleElements);
}
}

if (_shadowViewFactoryFuture != null) {
if (_shadowViewFactory == null) {
final f = _shadowViewFactoryFuture.then((ViewFactory viewFactory) =>
_insertView(viewFactory, shadowRoot, shadowScope, shadowInjector));
futures.add(f);
} else {
final f = new Future.microtask(() {
_insertView(_shadowViewFactory, shadowRoot, shadowScope, shadowInjector);
});
futures.add(f);
}
}

if (_componentFactory.config.elementProbeEnabled) {
probe = _componentFactory.expando[shadowDom] = shadowInjector.elementProbe;
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowDom] = null);
ElementProbe probe = _componentFactory.expando[shadowRoot] = shadowInjector.elementProbe;
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowRoot] = null);
}

var controller = shadowInjector.getByKey(_ref.typeKey);
Expand All @@ -155,6 +178,16 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
}
};
}

dom.Node _insertView(ViewFactory viewFactory,
dom.ShadowRoot shadowRoot,
Scope shadowScope,
ComponentDirectiveInjector shadowInjector) {
if (shadowScope.isAttached) {
shadowRoot.nodes.addAll(
viewFactory.call(shadowInjector.scope, shadowInjector).nodes);
}
}
}

@Injectable()
Expand Down
Loading

0 comments on commit 00960bb

Please sign in to comment.