vue-lazy-hydration
Advanced tools
Comparing version 0.1.0-alpha.0 to 0.1.0-alpha.2
@@ -1,3 +0,1 @@ | ||
import Vue from 'vue'; | ||
function _defineProperty(obj, key, value) { | ||
@@ -41,4 +39,5 @@ if (key in obj) { | ||
}; | ||
function ssrOnly(componentFactory) { | ||
if (process.server) return componentFactory; | ||
var NAMESPACE = "_lazyHydration"; | ||
function resolvableFactory(componentFactory) { | ||
var resolver; | ||
@@ -54,14 +53,53 @@ var resolverPromise = new Promise(function (resolve) { | ||
var factory = function factory() { | ||
var resolvable = function resolvable() { | ||
promise.then(function () { | ||
resolvable[NAMESPACE].resolve = undefined; | ||
}); | ||
return promise; | ||
}; | ||
Object.defineProperty(factory, "_lazyHydrationResolve", { | ||
value: resolver | ||
Object.defineProperty(resolvable, NAMESPACE, { | ||
value: { | ||
resolve: resolver | ||
} | ||
}); | ||
return factory; | ||
return resolvable; | ||
} | ||
function loadSsrOnly(componentFactory) { | ||
if (!process.browser) return componentFactory; | ||
var resolvable = resolvableFactory(componentFactory); | ||
return resolvable; | ||
} | ||
function loadWhenVisible(componentFactory, _ref) { | ||
var selector = _ref.selector; | ||
if (!process.browser || !("IntersectionObserver" in window)) { | ||
return componentFactory; | ||
} | ||
var resolvable = resolvableFactory(componentFactory); | ||
var elements = Array.from(document.querySelectorAll(selector)); | ||
var observer = new IntersectionObserver(function (entries) { | ||
if (!resolvable[NAMESPACE].resolve) return; // Use `intersectionRatio` because of Edge 15's | ||
// lack of support for `isIntersecting`. | ||
// See: https://github.com/w3c/IntersectionObserver/issues/211 | ||
var isIntersecting = !!entries.find(function (x) { | ||
return x.intersectionRatio > 0; | ||
}); | ||
if (!isIntersecting) return; | ||
elements.forEach(function (x) { | ||
return observer.unobserve(x); | ||
}); | ||
resolvable[NAMESPACE].resolve(); | ||
}); | ||
elements.forEach(function (x) { | ||
return observer.observe(x); | ||
}); | ||
return resolvable; | ||
} | ||
var VueLazyHydration = { | ||
install: function install(_, customOptions) { | ||
var _this = this; | ||
install: function install(Vue, customOptions) { | ||
if (!process.browser) return; | ||
@@ -79,6 +117,4 @@ var options = _objectSpread({}, DEFAULT_OPTIONS, customOptions); | ||
var hydrate = function hydrate(component) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
if (component._lazyHydrationResolve) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
component._lazyHydrationResolve(); | ||
if (component[NAMESPACE] && component[NAMESPACE].resolve) { | ||
component[NAMESPACE].resolve(); | ||
} | ||
@@ -89,3 +125,3 @@ }; | ||
Object.keys(components).map(function (x) { | ||
return _this.$options.components[x]; | ||
return components[x]; | ||
}).map(hydrate); | ||
@@ -96,9 +132,7 @@ }; | ||
beforeCreate: function beforeCreate() { | ||
// TODO exceptions? | ||
if (process.server || preventHydration) return; | ||
if (preventHydration) return; | ||
hydrateComponents(this.$options.components); | ||
}, | ||
beforeUpdate: function beforeUpdate() { | ||
// TODO exceptions? | ||
if (process.server || preventHydration) return; | ||
if (preventHydration) return; | ||
hydrateComponents(this.$options.components); | ||
@@ -110,2 +144,2 @@ } | ||
export { ssrOnly, VueLazyHydration }; | ||
export { loadSsrOnly, loadWhenVisible, VueLazyHydration }; |
@@ -1,1 +0,1 @@ | ||
import Vue from"vue";function _defineProperty(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function _objectSpread(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{},r=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(t).filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.forEach(function(n){_defineProperty(e,n,t[n])})}return e}var DEFAULT_OPTIONS={hydrationSwitch:Promise.resolve(!1),firstRenderTimeout:1e3};function ssrOnly(e){if(process.server)return e;var n,t=new Promise(function(e){n=e}),r=new Promise(function(n){t.then(function(){return n(e())})}),o=function(){return r};return Object.defineProperty(o,"_lazyHydrationResolve",{value:n}),o}var VueLazyHydration={install:function(e,n){var t=this,r=_objectSpread({},DEFAULT_OPTIONS,n),o=!0;r.hydrationSwitch.then(function(e){o=!e}),setTimeout(function(){o=!1},r.firstRenderTimeout);var i=function(e){e._lazyHydrationResolve&&e._lazyHydrationResolve()},c=function(e){Object.keys(e).map(function(e){return t.$options.components[e]}).map(i)};Vue.mixin({beforeCreate:function(){process.server||o||c(this.$options.components)},beforeUpdate:function(){process.server||o||c(this.$options.components)}})}};export{ssrOnly,VueLazyHydration}; | ||
function _defineProperty(e,r,n){return r in e?Object.defineProperty(e,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[r]=n,e}function _objectSpread(e){for(var r=1;r<arguments.length;r++){var n=null!=arguments[r]?arguments[r]:{},t=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(t=t.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),t.forEach(function(r){_defineProperty(e,r,n[r])})}return e}var DEFAULT_OPTIONS={hydrationSwitch:Promise.resolve(!1),firstRenderTimeout:1e3},NAMESPACE="_lazyHydration";function resolvableFactory(e){var r,n=new Promise(function(e){r=e}),t=new Promise(function(r){n.then(function(){return r(e())})}),o=function e(){return t.then(function(){e[NAMESPACE].resolve=void 0}),t};return Object.defineProperty(o,NAMESPACE,{value:{resolve:r}}),o}function loadSsrOnly(e){return process.browser?resolvableFactory(e):e}function loadWhenVisible(e,r){var n=r.selector;if(!(process.browser&&"IntersectionObserver"in window))return e;var t=resolvableFactory(e),o=Array.from(document.querySelectorAll(n)),i=new IntersectionObserver(function(e){t[NAMESPACE].resolve&&(!!e.find(function(e){return e.intersectionRatio>0})&&(o.forEach(function(e){return i.unobserve(e)}),t[NAMESPACE].resolve()))});return o.forEach(function(e){return i.observe(e)}),t}var VueLazyHydration={install:function(e,r){if(process.browser){var n=_objectSpread({},DEFAULT_OPTIONS,r),t=!0;n.hydrationSwitch.then(function(e){t=!e}),setTimeout(function(){t=!1},n.firstRenderTimeout);var o=function(e){e[NAMESPACE]&&e[NAMESPACE].resolve&&e[NAMESPACE].resolve()},i=function(e){Object.keys(e).map(function(r){return e[r]}).map(o)};e.mixin({beforeCreate:function(){t||i(this.$options.components)},beforeUpdate:function(){t||i(this.$options.components)}})}}};export{loadSsrOnly,loadWhenVisible,VueLazyHydration}; |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) : | ||
(global = global || self, factory(global['vue-lazy-hydration'] = {}, global.Vue)); | ||
}(this, function (exports, Vue) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global['vue-lazy-hydration'] = {})); | ||
}(this, function (exports) { 'use strict'; | ||
Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue; | ||
function _defineProperty(obj, key, value) { | ||
@@ -47,4 +45,5 @@ if (key in obj) { | ||
}; | ||
function ssrOnly(componentFactory) { | ||
if (process.server) return componentFactory; | ||
var NAMESPACE = "_lazyHydration"; | ||
function resolvableFactory(componentFactory) { | ||
var resolver; | ||
@@ -60,14 +59,53 @@ var resolverPromise = new Promise(function (resolve) { | ||
var factory = function factory() { | ||
var resolvable = function resolvable() { | ||
promise.then(function () { | ||
resolvable[NAMESPACE].resolve = undefined; | ||
}); | ||
return promise; | ||
}; | ||
Object.defineProperty(factory, "_lazyHydrationResolve", { | ||
value: resolver | ||
Object.defineProperty(resolvable, NAMESPACE, { | ||
value: { | ||
resolve: resolver | ||
} | ||
}); | ||
return factory; | ||
return resolvable; | ||
} | ||
function loadSsrOnly(componentFactory) { | ||
if (!process.browser) return componentFactory; | ||
var resolvable = resolvableFactory(componentFactory); | ||
return resolvable; | ||
} | ||
function loadWhenVisible(componentFactory, _ref) { | ||
var selector = _ref.selector; | ||
if (!process.browser || !("IntersectionObserver" in window)) { | ||
return componentFactory; | ||
} | ||
var resolvable = resolvableFactory(componentFactory); | ||
var elements = Array.from(document.querySelectorAll(selector)); | ||
var observer = new IntersectionObserver(function (entries) { | ||
if (!resolvable[NAMESPACE].resolve) return; // Use `intersectionRatio` because of Edge 15's | ||
// lack of support for `isIntersecting`. | ||
// See: https://github.com/w3c/IntersectionObserver/issues/211 | ||
var isIntersecting = !!entries.find(function (x) { | ||
return x.intersectionRatio > 0; | ||
}); | ||
if (!isIntersecting) return; | ||
elements.forEach(function (x) { | ||
return observer.unobserve(x); | ||
}); | ||
resolvable[NAMESPACE].resolve(); | ||
}); | ||
elements.forEach(function (x) { | ||
return observer.observe(x); | ||
}); | ||
return resolvable; | ||
} | ||
var VueLazyHydration = { | ||
install: function install(_, customOptions) { | ||
var _this = this; | ||
install: function install(Vue, customOptions) { | ||
if (!process.browser) return; | ||
@@ -85,6 +123,4 @@ var options = _objectSpread({}, DEFAULT_OPTIONS, customOptions); | ||
var hydrate = function hydrate(component) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
if (component._lazyHydrationResolve) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
component._lazyHydrationResolve(); | ||
if (component[NAMESPACE] && component[NAMESPACE].resolve) { | ||
component[NAMESPACE].resolve(); | ||
} | ||
@@ -95,3 +131,3 @@ }; | ||
Object.keys(components).map(function (x) { | ||
return _this.$options.components[x]; | ||
return components[x]; | ||
}).map(hydrate); | ||
@@ -102,9 +138,7 @@ }; | ||
beforeCreate: function beforeCreate() { | ||
// TODO exceptions? | ||
if (process.server || preventHydration) return; | ||
if (preventHydration) return; | ||
hydrateComponents(this.$options.components); | ||
}, | ||
beforeUpdate: function beforeUpdate() { | ||
// TODO exceptions? | ||
if (process.server || preventHydration) return; | ||
if (preventHydration) return; | ||
hydrateComponents(this.$options.components); | ||
@@ -116,3 +150,4 @@ } | ||
exports.ssrOnly = ssrOnly; | ||
exports.loadSsrOnly = loadSsrOnly; | ||
exports.loadWhenVisible = loadWhenVisible; | ||
exports.VueLazyHydration = VueLazyHydration; | ||
@@ -119,0 +154,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],n):n((e=e||self)["vue-lazy-hydration"]={},e.Vue)}(this,function(e,n){"use strict";function t(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}n=n&&n.hasOwnProperty("default")?n.default:n;var r={hydrationSwitch:Promise.resolve(!1),firstRenderTimeout:1e3};var o={install:function(e,o){var i=this,u=function(e){for(var n=1;n<arguments.length;n++){var r=null!=arguments[n]?arguments[n]:{},o=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),o.forEach(function(n){t(e,n,r[n])})}return e}({},r,o),s=!0;u.hydrationSwitch.then(function(e){s=!e}),setTimeout(function(){s=!1},u.firstRenderTimeout);var c=function(e){e._lazyHydrationResolve&&e._lazyHydrationResolve()},f=function(e){Object.keys(e).map(function(e){return i.$options.components[e]}).map(c)};n.mixin({beforeCreate:function(){process.server||s||f(this.$options.components)},beforeUpdate:function(){process.server||s||f(this.$options.components)}})}};e.ssrOnly=function(e){if(process.server)return e;var n,t=new Promise(function(e){n=e}),r=new Promise(function(n){t.then(function(){return n(e())})}),o=function(){return r};return Object.defineProperty(o,"_lazyHydrationResolve",{value:n}),o},e.VueLazyHydration=o,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e=e||self)["vue-lazy-hydration"]={})}(this,function(e){"use strict";function n(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}var r={hydrationSwitch:Promise.resolve(!1),firstRenderTimeout:1e3},t="_lazyHydration";function o(e){var n,r=new Promise(function(e){n=e}),o=new Promise(function(n){r.then(function(){return n(e())})}),i=function e(){return o.then(function(){e[t].resolve=void 0}),o};return Object.defineProperty(i,t,{value:{resolve:n}}),i}var i={install:function(e,o){if(process.browser){var i=function(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{},o=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(t).filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),o.forEach(function(r){n(e,r,t[r])})}return e}({},r,o),u=!0;i.hydrationSwitch.then(function(e){u=!e}),setTimeout(function(){u=!1},i.firstRenderTimeout);var c=function(e){e[t]&&e[t].resolve&&e[t].resolve()},f=function(e){Object.keys(e).map(function(n){return e[n]}).map(c)};e.mixin({beforeCreate:function(){u||f(this.$options.components)},beforeUpdate:function(){u||f(this.$options.components)}})}}};e.loadSsrOnly=function(e){return process.browser?o(e):e},e.loadWhenVisible=function(e,n){var r=n.selector;if(!(process.browser&&"IntersectionObserver"in window))return e;var i=o(e),u=Array.from(document.querySelectorAll(r)),c=new IntersectionObserver(function(e){i[t].resolve&&e.find(function(e){return e.intersectionRatio>0})&&(u.forEach(function(e){return c.unobserve(e)}),i[t].resolve())});return u.forEach(function(e){return c.observe(e)}),i},e.VueLazyHydration=i,Object.defineProperty(e,"__esModule",{value:!0})}); |
{ | ||
"name": "vue-lazy-hydration", | ||
"version": "0.1.0-alpha.0", | ||
"version": "0.1.0-alpha.2", | ||
"description": "Lazy hydration of server-side rendered Vue.js components", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -8,4 +8,8 @@ # vue-lazy-hydration | ||
Lazy hydration of server-side rendered Vue.js components. | ||
> Lazy hydration of server-side rendered Vue.js components. | ||
## Motivation | ||
vue-lazy-hydration is a component to **improve Estimated Input Latency and Time to Interactive** of server-side rendered Vue.js applications. This can be achieved **by using lazy hydration to delay the hydration of pre-rendered HTML**. Additionally, **code splitting is used to delay the loading of the JavaScript code of components** which are marked for lazy hydration. | ||
## Install | ||
@@ -17,2 +21,43 @@ | ||
### Basic example | ||
In the example below you can see the three `load` modi in action. | ||
1. The `ArticleContent` component is only loaded in SSR mode, which means it never gets hydrated in the browser, which also means it will never be interactive (static content only). | ||
2. Next we can see the `AddSlider` beneath the article content, this component will most likely not be visible initially so we can delay hydration until the point it becomes visible. | ||
3. At the very bottom of the page we want to render a `CommentForm` but because most people only read the article and don't leave a comment, we can save resources by only hydrating the component whenever it actually receives focus. | ||
```html | ||
<template> | ||
<div class="ArticlePage"> | ||
<ArticleContent :content="article.content"/> | ||
<AddSlider/> | ||
<CommentForm :article-id="article.id"/> | ||
</div> | ||
</template> | ||
<script> | ||
import { | ||
loadOnInteraction, | ||
loadSsrOnly, | ||
loadWhenVisible, | ||
} from 'vue-lazy-hydration'; | ||
export default { | ||
components: { | ||
AddSlider: loadWhenVisible( | ||
() => import('./AddSlider.vue'), | ||
{ selector: `.AddSlider` }, | ||
), | ||
ArticleContent: loadSsrOnly(() => import('./ArticleContent.vue')), | ||
CommentForm: loadOnInteraction( | ||
() => import('./CommentForm.vue'), | ||
{ event: 'focus' }, | ||
), | ||
}, | ||
// ... | ||
}; | ||
</script> | ||
``` | ||
## About | ||
@@ -19,0 +64,0 @@ |
@@ -1,4 +0,1 @@ | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
import Vue from 'vue'; | ||
const DEFAULT_OPTIONS = { | ||
@@ -8,6 +5,5 @@ hydrationSwitch: Promise.resolve(false), | ||
}; | ||
const NAMESPACE = `_lazyHydration`; | ||
export function ssrOnly(componentFactory) { | ||
if (process.server) return componentFactory; | ||
function resolvableFactory(componentFactory) { | ||
let resolver; | ||
@@ -21,13 +17,54 @@ const resolverPromise = new Promise((resolve) => { | ||
const factory = () => promise; | ||
const resolvable = () => { | ||
promise.then(() => { | ||
resolvable[NAMESPACE].resolve = undefined; | ||
}); | ||
Object.defineProperty(factory, `_lazyHydrationResolve`, { | ||
value: resolver, | ||
return promise; | ||
}; | ||
Object.defineProperty(resolvable, NAMESPACE, { | ||
value: { | ||
resolve: resolver, | ||
}, | ||
}); | ||
return factory; | ||
return resolvable; | ||
} | ||
export function loadSsrOnly(componentFactory) { | ||
if (!process.browser) return componentFactory; | ||
const resolvable = resolvableFactory(componentFactory); | ||
return resolvable; | ||
} | ||
export function loadWhenVisible(componentFactory, { selector }) { | ||
if (!process.browser || !(`IntersectionObserver` in window)) { | ||
return componentFactory; | ||
} | ||
const resolvable = resolvableFactory(componentFactory); | ||
const elements = Array.from(document.querySelectorAll(selector)); | ||
const observer = new IntersectionObserver((entries) => { | ||
if (!resolvable[NAMESPACE].resolve) return; | ||
// Use `intersectionRatio` because of Edge 15's | ||
// lack of support for `isIntersecting`. | ||
// See: https://github.com/w3c/IntersectionObserver/issues/211 | ||
const isIntersecting = !!entries.find(x => x.intersectionRatio > 0); | ||
if (!isIntersecting) return; | ||
elements.forEach(x => observer.unobserve(x)); | ||
resolvable[NAMESPACE].resolve(); | ||
}); | ||
elements.forEach(x => observer.observe(x)); | ||
return resolvable; | ||
} | ||
export const VueLazyHydration = { | ||
install(_, customOptions) { | ||
install(Vue, customOptions) { | ||
if (!process.browser) return; | ||
const options = { | ||
@@ -49,6 +86,4 @@ ...DEFAULT_OPTIONS, | ||
const hydrate = (component) => { | ||
// eslint-disable-next-line no-underscore-dangle | ||
if (component._lazyHydrationResolve) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
component._lazyHydrationResolve(); | ||
if (component[NAMESPACE] && component[NAMESPACE].resolve) { | ||
component[NAMESPACE].resolve(); | ||
} | ||
@@ -58,3 +93,3 @@ }; | ||
Object.keys(components) | ||
.map(x => this.$options.components[x]) | ||
.map(x => components[x]) | ||
.map(hydrate); | ||
@@ -65,9 +100,7 @@ }; | ||
beforeCreate() { | ||
// TODO exceptions? | ||
if (process.server || preventHydration) return; | ||
if (preventHydration) return; | ||
hydrateComponents(this.$options.components); | ||
}, | ||
beforeUpdate() { | ||
// TODO exceptions? | ||
if (process.server || preventHydration) return; | ||
if (preventHydration) return; | ||
hydrateComponents(this.$options.components); | ||
@@ -74,0 +107,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
127942
366
1
74