Skip to content

Commit

Permalink
Lazy register features we can be deferred until first instance. This …
Browse files Browse the repository at this point in the history
…is an optimization which can speed up page load time when elements are registered but not needed at time of first paint/interaction
  • Loading branch information
Steven Orvell committed Mar 14, 2016
1 parent edb59eb commit 31702ff
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 38 deletions.
76 changes: 76 additions & 0 deletions lazy-register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!doctype html>
<html>
<head>

<title>lazy-register</title>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
Polymer = {lazyRegistration: true};
</script>
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="../../polymer.html">

</head>
<body>

<script>
(function() {
var lr = Polymer.Base._ensureRegistered;
Polymer.Base._ensureRegistered = function(proto) {
console.log('_ensureRegistered', proto.is);
lr.call(this, proto);
}

})();
</script>

<dom-module id="x-base">
<script>
Polymer({

is: 'x-base',

properties: {
state: {value: 'live!'}
}

});
</script>
</dom-module>

<dom-module id="x-foo">
<template>I {{state}}</template>
<script>
XFoo = Polymer({

is: 'x-foo',
extends: 'x-base'

});
</script>
</dom-module>

<dom-module id="x-bar">
<script>
Polymer({

is: 'x-bar',
extends: 'x-foo'

});
</script>
</dom-module>


<script>
// console.log('XFoo has performed lazy registration tasks', Boolean(XFoo.prototype._template));
// console.log('Create XFoo', new XFoo());
// console.log('XFoo has performed lazy registration tasks', Boolean(XFoo.prototype._template));
</script>

<!-- <x-bar></x-bar> -->

</body>
</html>
6 changes: 5 additions & 1 deletion polymer.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
this._prepConstructor();
// template
this._prepTemplate();
// styles and style properties
// styles
this._prepStyles();
},

_registerLazyFeatures: function() {
this._prepShimStyles();
// template markup
this._prepAnnotations();
// accessors
Expand Down
22 changes: 21 additions & 1 deletion src/lib/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,36 @@
this._desugarBehaviors(); // abstract
this._doBehavior('beforeRegister'); // abstract
this._registerFeatures(); // abstract
this._doBehavior('registered'); // abstract
},

createdCallback: function() {
this._ensureRegistered(this.__proto__);
Polymer.telemetry.instanceCount++;
this.root = this;
this._doBehavior('created'); // abstract
this._initFeatures(); // abstract
},

/**
* When called from the element's prototype, ensures that the element has
* fully registered. By default registration tasks are defered until the
* first instance of an element is created.
*/
ensureRegistered: function() {
this._ensureRegistered(this);
},

_ensureRegistered: function(proto) {
if (proto.__hasRegistered !== proto.is) {
proto.__hasRegistered = proto.is;
if (proto._registerLazyFeatures) {
proto._registerLazyFeatures();
}
// registration extension point
this._doBehavior('registered');
}
},

// reserved for canonical behavior
attachedCallback: function() {
// NOTE: workaround for:
Expand Down
32 changes: 24 additions & 8 deletions src/lib/style-util.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,38 @@
},

// add a string of cssText to the document.
applyCss: function(cssText, moniker, target, afterNode) {
applyCss: function(cssText, moniker, target, contextNode) {
var style = this.createScopeStyle(cssText, moniker);
target = target || document.head;
var after = (contextNode && contextNode.nextSibling) ||
target.firstChild;
this.__lastHeadApplyNode = style;
return target.insertBefore(style, after);
},

createScopeStyle: function(cssText, moniker) {
var style = document.createElement('style');
if (moniker) {
style.setAttribute('scope', moniker);
}
style.textContent = cssText;
target = target || document.head;
if (!afterNode) {
var n$ = target.querySelectorAll('style[scope]');
afterNode = n$[n$.length-1];
}
target.insertBefore(style,
(afterNode && afterNode.nextSibling) || target.firstChild);
return style;
},

__lastHeadApplyNode: null,

// insert a comment node as a styling position placeholder.
applyStylePlaceHolder: function(moniker) {
var placeHolder = document.createComment(' polymer element ' +
moniker + ' ');
var after = this.__lastHeadApplyNode ?
this.__lastHeadApplyNode.nextSibling : null;
var scope = document.head;
scope.insertBefore(placeHolder, after || scope.firstChild);
this.__lastHeadApplyNode = placeHolder;
return placeHolder;
},

cssFromModules: function(moduleIds, warnIfNotFound) {
var modules = moduleIds.trim().split(' ');
var cssText = '';
Expand Down
27 changes: 18 additions & 9 deletions src/standard/styling.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@
}
if (this._template) {
this._styles = this._collectStyles();
// under shady dom we always output a shimmed style (which may be
// empty) so that other dynamic stylesheets can always be placed
// after the element's main stylesheet.
// This helps ensure element styles are always in registration order.
if (this._styles.length && !nativeShadow) {
this._scopeStyle = styleUtil.applyStylePlaceHolder(this.is);
}
}
},

_prepShimStyles: function() {
if (this._template) {
// calculate shimmed styles (we must always do this as it
// stores shimmed style data in the css rules for later use)
var cssText = styleTransformer.elementStyles(this);
Expand All @@ -50,21 +62,18 @@
// do we really need to output static shimmed styles?
// only if no custom properties are used since otherwise
// styles are applied via property shimming.
var needsStatic = this._styles.length &&
!this._needsStyleProperties();
// under shady dom we always output a shimmed style (which may be
// empty) so that other dynamic stylesheets can always be placed
// after the element's main stylesheet.
// This helps ensure element styles are always in registration order.
if (needsStatic || !nativeShadow) {
if (!this._needsStyleProperties() && this._styles.length) {
// NOTE: IE has css style ordering issues unless there's at least a
// space in the stylesheet.
cssText = needsStatic ? cssText : ' ';
var style = styleUtil.applyCss(cssText, this.is,
nativeShadow ? this._template.content : null);
nativeShadow ? this._template.content : null, this._scopeStyle);
// keep track of style when in document scope (polyfill) so we can
// attach property styles after it.
if (!nativeShadow) {
// remove old scope style (comment node) when it's not needed.
if (this._scopeStyle) {
this._scopeStyle.parentNode.removeChild(this._scopeStyle);
}
this._scopeStyle = style;
}
}
Expand Down
20 changes: 2 additions & 18 deletions test/unit/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@

setup(function() {
// Ensure a clean environment for each test.
/* global Base */
window.Base = Polymer.Base;
window.Child = Object.create(Base);
Child._registerFeatures = function() {};
Child._registerFeatures = function() {
};
Child._initFeatures = function() {};
Child._setAttributeToProperty = function() {};
Child._desugarBehaviors = function() {};
Expand All @@ -54,22 +54,6 @@

});

suite('registerCallback', function() {

test('calls registered() after registerFeatures()', function() {
var called = [];
Child._registerFeatures = function() {
called.push('1');
};
Child.registered = function() {
called.push('2');
};
assert.deepEqual(called, []);
Child.registerCallback();
assert.includeMembers(called, ['1', '2']);
});

});

suite('createdCallback', function() {

Expand Down
2 changes: 1 addition & 1 deletion test/unit/notify-path.html
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@
Polymer({
is: 'x-broken',
observers: ['foo(missingParenthesis']
});
}).prototype.ensureRegistered();
} catch (e) {
assert.equal(e.message, "Malformed observer expression 'foo(missingParenthesis'");
thrown = true;
Expand Down

0 comments on commit 31702ff

Please sign in to comment.