vue-lazy-hydration
Advanced tools
Comparing version 2.0.0-alpha.0 to 2.0.0-beta.0
@@ -51,3 +51,3 @@ function _defineProperty(obj, key, value) { | ||
var observers = new Map(); | ||
function createObserver(options) { | ||
function makeHydrationObserver(options) { | ||
if (typeof IntersectionObserver === "undefined") return null; | ||
@@ -69,162 +69,206 @@ var optionKey = JSON.stringify(options); | ||
} | ||
function loadingComponentFactory(resolvableComponent, options) { | ||
return _objectSpread2({ | ||
render: function render(h) { | ||
var tag = this.$el ? this.$el.tagName : "div"; // eslint-disable-next-line no-underscore-dangle | ||
if (!this.$el) resolvableComponent._resolve(); | ||
return h(tag); | ||
} | ||
}, options); | ||
} | ||
function resolvableComponentFactory(component) { | ||
var resolve; | ||
var promise = new Promise(function (newResolve) { | ||
resolve = newResolve; | ||
}); // eslint-disable-next-line no-underscore-dangle | ||
function makeHydrationPromise() { | ||
var hydrate = function hydrate() {}; | ||
promise._resolve = function () { | ||
resolve(typeof component === "function" ? component() : component); | ||
var hydrationPromise = new Promise(function (resolve) { | ||
hydrate = resolve; | ||
}); | ||
return { | ||
hydrate: hydrate, | ||
hydrationPromise: hydrationPromise | ||
}; | ||
return promise; | ||
} | ||
var isServer = typeof window === "undefined"; | ||
function hydrateWhenIdle(component) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
ignoredProps = _ref.ignoredProps; | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted: function mounted() { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!("requestIdleCallback" in window) || !("requestAnimationFrame" in window)) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
resolvableComponent._resolve(); | ||
function isAsyncComponentFactory(componentOrFactory) { | ||
return typeof componentOrFactory === "function"; | ||
} | ||
return; | ||
} | ||
function resolveComponent(componentOrFactory) { | ||
if (isAsyncComponentFactory(componentOrFactory)) { | ||
return componentOrFactory().then(function (componentModule) { | ||
return componentModule.default; | ||
}); | ||
} | ||
var id = requestIdleCallback(function () { | ||
// eslint-disable-next-line no-underscore-dangle | ||
requestAnimationFrame(resolvableComponent._resolve); | ||
}, { | ||
timeout: this.idleTimeout | ||
}); | ||
return componentOrFactory; | ||
} | ||
var cleanup = function cleanup() { | ||
return cancelIdleCallback(id); | ||
}; | ||
resolvableComponent.then(cleanup); | ||
} | ||
}); | ||
function makeNonce(_ref) { | ||
var component = _ref.component, | ||
hydrate = _ref.hydrate, | ||
hydrationPromise = _ref.hydrationPromise; | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
return new Promise(function (resolve) { | ||
if (isServer) hydrate(); | ||
hydrationPromise.then(function () { | ||
return resolve(resolveComponent(component)); | ||
}); | ||
}); | ||
}; | ||
} | ||
function hydrateWhenVisible(component) { | ||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
ignoredProps = _ref2.ignoredProps, | ||
observerOptions = _ref2.observerOptions; | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var observer = createObserver(observerOptions); | ||
var loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted: function mounted() { | ||
var _this = this; | ||
function makeHydrationBlocker(component, options) { | ||
return _objectSpread2(_objectSpread2({}, options), {}, { | ||
mixins: [{ | ||
beforeCreate: function beforeCreate() { | ||
this.cleanupHandlers = []; | ||
// If Intersection Observer API is not supported, hydrate immediately. | ||
if (!observer) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
resolvableComponent._resolve(); | ||
var _makeHydrationPromise = makeHydrationPromise(), | ||
hydrate = _makeHydrationPromise.hydrate, | ||
hydrationPromise = _makeHydrationPromise.hydrationPromise; | ||
return; | ||
} // eslint-disable-next-line no-underscore-dangle | ||
this.Nonce = makeNonce({ | ||
component: component, | ||
hydrate: hydrate, | ||
hydrationPromise: hydrationPromise | ||
}); | ||
this.hydrate = hydrate; | ||
this.hydrationPromise = hydrationPromise; | ||
}, | ||
beforeDestroy: function beforeDestroy() { | ||
this.cleanup(); | ||
}, | ||
mounted: function mounted() { | ||
var _this = this; | ||
if (this.$el.nodeType === Node.COMMENT_NODE) { | ||
// No SSR rendered content, hydrate immediately. | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = resolvableComponent._resolve; | ||
if (this.never) return; | ||
var cleanup = function cleanup() { | ||
return observer.unobserve(_this.$el); | ||
}; | ||
if (this.whenVisible) { | ||
var observerOptions = this.whenVisible !== true ? this.whenVisible : undefined; | ||
var observer = makeHydrationObserver(observerOptions); | ||
resolvableComponent.then(cleanup); | ||
observer.observe(this.$el); | ||
if (!observer) { | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = this.hydrate; | ||
var cleanup = function cleanup() { | ||
return observer.unobserve(_this.$el); | ||
}; | ||
this.hydrationPromise.then(cleanup); | ||
observer.observe(this.$el); | ||
return; | ||
} | ||
if (this.whenIdle) { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!("requestIdleCallback" in window) || !("requestAnimationFrame" in window)) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
this.hydrate(); | ||
return; | ||
} // @ts-ignore | ||
var id = requestIdleCallback(function () { | ||
// eslint-disable-next-line no-underscore-dangle | ||
requestAnimationFrame(_this.hydrate); | ||
}, { | ||
timeout: this.idleTimeout | ||
}); // @ts-ignore | ||
var _cleanup = function _cleanup() { | ||
return cancelIdleCallback(id); | ||
}; | ||
this.hydrationPromise.then(_cleanup); | ||
} | ||
if (this.interactionEvents.length) { | ||
var eventListenerOptions = { | ||
capture: true, | ||
once: true, | ||
passive: true | ||
}; | ||
this.interactionEvents.forEach(function (eventName) { | ||
var _this$$el; | ||
var eventListenerParams = [eventName, _this.hydrate, eventListenerOptions]; | ||
(_this$$el = _this.$el).addEventListener.apply(_this$$el, eventListenerParams); | ||
_this.cleanupHandlers.push(function () { | ||
var _this$$el2; | ||
return (_this$$el2 = _this.$el).removeEventListener.apply(_this$$el2, eventListenerParams); | ||
}); | ||
}); | ||
} | ||
}, | ||
methods: { | ||
cleanup: function cleanup() { | ||
this.cleanupHandlers.forEach(function (handler) { | ||
return handler(); | ||
}); | ||
} | ||
}, | ||
render: function render(h) { | ||
return h(this.Nonce, { | ||
props: this.$attrs | ||
}, this.$slots.default); | ||
} | ||
}] | ||
}); | ||
} | ||
function hydrateWhenIdle(componentOrFactory) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$timeout = _ref.timeout, | ||
timeout = _ref$timeout === void 0 ? 2000 : _ref$timeout; | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.whenIdle = true; | ||
this.idleTimeout = timeout; | ||
} | ||
}); | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
}; | ||
} | ||
function hydrateNever(component) { | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var loading = loadingComponentFactory(resolvableComponent); | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
}; | ||
function hydrateWhenVisible(componentOrFactory) { | ||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref2$observerOptions = _ref2.observerOptions, | ||
observerOptions = _ref2$observerOptions === void 0 ? undefined : _ref2$observerOptions; | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.whenVisible = observerOptions || true; | ||
} | ||
}); | ||
} | ||
function hydrateOnInteraction(component) { | ||
function hydrateNever(componentOrFactory) { | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.never = true; | ||
} | ||
}); | ||
} | ||
function hydrateOnInteraction(componentOrFactory) { | ||
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref3$event = _ref3.event, | ||
event = _ref3$event === void 0 ? "focus" : _ref3$event, | ||
ignoredProps = _ref3.ignoredProps; | ||
event = _ref3$event === void 0 ? "focus" : _ref3$event; | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var events = Array.isArray(event) ? event : [event]; | ||
var loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted: function mounted() { | ||
var _this2 = this; | ||
events.forEach(function (eventName) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
_this2.$el.addEventListener(eventName, resolvableComponent._resolve, { | ||
capture: true, | ||
once: true, | ||
passive: true | ||
}); | ||
}); | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.interactionEvents = events; | ||
} | ||
}); | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
}; | ||
} | ||
var Nonce = function Nonce() { | ||
return new Promise(function () {}); | ||
}; | ||
var LazyHydrateBlocker = { | ||
functional: true, | ||
render: function render(h, context) { | ||
return context.props.isHydrated ? context.props.content : h(Nonce); | ||
var Placeholder = { | ||
render: function render() { | ||
return this.$slots.default; | ||
} | ||
}; | ||
var LazyHydrate = { | ||
var LazyHydrate = makeHydrationBlocker(Placeholder, { | ||
props: { | ||
@@ -235,8 +279,8 @@ idleTimeout: { | ||
}, | ||
never: { | ||
type: Boolean | ||
}, | ||
onInteraction: { | ||
type: [Array, Boolean, String] | ||
}, | ||
never: { | ||
type: Boolean | ||
}, | ||
triggerHydration: { | ||
@@ -253,15 +297,2 @@ default: false, | ||
}, | ||
data: function data() { | ||
return { | ||
isHydrated: isServer | ||
}; | ||
}, | ||
watch: { | ||
triggerHydration: { | ||
immediate: true, | ||
handler: function handler(hydrate) { | ||
if (hydrate) this.hydrate(); | ||
} | ||
} | ||
}, | ||
computed: { | ||
@@ -274,99 +305,13 @@ interactionEvents: function interactionEvents() { | ||
}, | ||
mounted: function mounted() { | ||
var _this3 = this; | ||
if (this.$el.childElementCount === 0) { | ||
// No SSR rendered content, hydrate immediately. | ||
this.hydrate(); | ||
return; | ||
} | ||
if (this.never) return; | ||
this.interactionEvents.forEach(function (eventName) { | ||
_this3.$el.addEventListener(eventName, _this3.hydrate, { | ||
capture: true, | ||
once: true, | ||
passive: true | ||
}); | ||
}); | ||
if (this.interactionEvents.length) { | ||
this.interaction = function () { | ||
_this3.interactionEvents.forEach(function (eventName) { | ||
return _this3.$el.removeEventListener(eventName, _this3.hydrate); | ||
}); | ||
}; | ||
} | ||
if (this.whenIdle) { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!("requestIdleCallback" in window) || !("requestAnimationFrame" in window)) { | ||
this.hydrate(); | ||
return; | ||
watch: { | ||
triggerHydration: { | ||
immediate: true, | ||
handler: function handler(isTriggered) { | ||
if (isTriggered) this.hydrate(); | ||
} | ||
var id = requestIdleCallback(function () { | ||
requestAnimationFrame(function () { | ||
_this3.hydrate(); | ||
}); | ||
}, { | ||
timeout: this.idleTimeout | ||
}); | ||
this.idle = function () { | ||
return cancelIdleCallback(id); | ||
}; | ||
} | ||
if (this.whenVisible) { | ||
var options = this.whenVisible === true ? {} : this.whenVisible; | ||
var observer = createObserver(options); // If Intersection Observer API is not supported, hydrate immediately. | ||
if (!observer) { | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = this.hydrate; | ||
observer.observe(this.$el); | ||
this.visible = function () { | ||
observer.unobserve(_this3.$el); | ||
delete _this3.$el.hydrate; | ||
}; | ||
} | ||
}, | ||
beforeDestroy: function beforeDestroy() { | ||
this.cleanup(); | ||
}, | ||
methods: { | ||
cleanup: function cleanup() { | ||
var _this4 = this; | ||
var handlers = ["idle", "interaction", "visible"]; | ||
handlers.forEach(function (handler) { | ||
if (handler in _this4) { | ||
_this4[handler](); | ||
delete _this4[handler]; | ||
} | ||
}); | ||
}, | ||
hydrate: function hydrate() { | ||
this.isHydrated = true; | ||
this.cleanup(); | ||
} | ||
}, | ||
render: function render(h) { | ||
return h(LazyHydrateBlocker, { | ||
props: { | ||
content: this.$slots.default, | ||
isHydrated: this.isHydrated | ||
} | ||
}); | ||
} | ||
}; | ||
}); | ||
export default LazyHydrate; | ||
export { hydrateNever, hydrateOnInteraction, hydrateWhenIdle, hydrateWhenVisible }; |
@@ -1,1 +0,1 @@ | ||
function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ownKeys(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function _objectSpread2(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?ownKeys(Object(n),!0).forEach(function(t){_defineProperty(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):ownKeys(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}var observers=new Map;function createObserver(e){if("undefined"==typeof IntersectionObserver)return null;var t=JSON.stringify(e);if(observers.has(t))return observers.get(t);var n=new IntersectionObserver(function(e){e.forEach(function(e){(e.isIntersecting||e.intersectionRatio>0)&&e.target.hydrate&&e.target.hydrate()})},e);return observers.set(t,n),n}function loadingComponentFactory(e,t){return _objectSpread2({render:function(t){var n=this.$el?this.$el.tagName:"div";return this.$el||e._resolve(),t(n)}},t)}function resolvableComponentFactory(e){var t,n=new Promise(function(e){t=e});return n._resolve=function(){t("function"==typeof e?e():e)},n}var isServer="undefined"==typeof window;function hydrateWhenIdle(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).ignoredProps;if(isServer)return e;var n=resolvableComponentFactory(e),r=loadingComponentFactory(n,{props:t,mounted:function(){if("requestIdleCallback"in window&&"requestAnimationFrame"in window){var e=requestIdleCallback(function(){requestAnimationFrame(n._resolve)},{timeout:this.idleTimeout});n.then(function(){return cancelIdleCallback(e)})}else n._resolve()}});return function(){return{component:n,delay:0,loading:r}}}function hydrateWhenVisible(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.ignoredProps,r=t.observerOptions;if(isServer)return e;var o=resolvableComponentFactory(e),i=createObserver(r),a=loadingComponentFactory(o,{props:n,mounted:function(){var e=this;if(i){this.$el.hydrate=o._resolve;o.then(function(){return i.unobserve(e.$el)}),i.observe(this.$el)}else o._resolve()}});return function(){return{component:o,delay:0,loading:a}}}function hydrateNever(e){if(isServer)return e;var t=resolvableComponentFactory(e),n=loadingComponentFactory(t);return function(){return{component:t,delay:0,loading:n}}}function hydrateOnInteraction(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.event,r=void 0===n?"focus":n,o=t.ignoredProps;if(isServer)return e;var i=resolvableComponentFactory(e),a=Array.isArray(r)?r:[r],s=loadingComponentFactory(i,{props:o,mounted:function(){var e=this;a.forEach(function(t){e.$el.addEventListener(t,i._resolve,{capture:!0,once:!0,passive:!0})})}});return function(){return{component:i,delay:0,loading:s}}}var Nonce=function(){return new Promise(function(){})},LazyHydrateBlocker={functional:!0,render:function(e,t){return t.props.isHydrated?t.props.content:e(Nonce)}},LazyHydrate={props:{idleTimeout:{default:2e3,type:Number},onInteraction:{type:[Array,Boolean,String]},never:{type:Boolean},triggerHydration:{default:!1,type:Boolean},whenIdle:{type:Boolean},whenVisible:{type:[Boolean,Object]}},data:function(){return{isHydrated:isServer}},watch:{triggerHydration:{immediate:!0,handler:function(e){e&&this.hydrate()}}},computed:{interactionEvents:function(){return this.onInteraction?!0===this.onInteraction?["focus"]:Array.isArray(this.onInteraction)?this.onInteraction:[this.onInteraction]:[]}},mounted:function(){var e=this;if(0!==this.$el.childElementCount){if(!this.never){if(this.interactionEvents.forEach(function(t){e.$el.addEventListener(t,e.hydrate,{capture:!0,once:!0,passive:!0})}),this.interactionEvents.length&&(this.interaction=function(){e.interactionEvents.forEach(function(t){return e.$el.removeEventListener(t,e.hydrate)})}),this.whenIdle){if(!("requestIdleCallback"in window&&"requestAnimationFrame"in window))return void this.hydrate();var t=requestIdleCallback(function(){requestAnimationFrame(function(){e.hydrate()})},{timeout:this.idleTimeout});this.idle=function(){return cancelIdleCallback(t)}}if(this.whenVisible){var n=createObserver(!0===this.whenVisible?{}:this.whenVisible);if(!n)return void this.hydrate();this.$el.hydrate=this.hydrate,n.observe(this.$el),this.visible=function(){n.unobserve(e.$el),delete e.$el.hydrate}}}}else this.hydrate()},beforeDestroy:function(){this.cleanup()},methods:{cleanup:function(){var e=this;["idle","interaction","visible"].forEach(function(t){t in e&&(e[t](),delete e[t])})},hydrate:function(){this.isHydrated=!0,this.cleanup()}},render:function(e){return e(LazyHydrateBlocker,{props:{content:this.$slots.default,isHydrated:this.isHydrated}})}};export default LazyHydrate;export{hydrateNever,hydrateOnInteraction,hydrateWhenIdle,hydrateWhenVisible}; | ||
function _defineProperty(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function ownKeys(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function _objectSpread2(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?ownKeys(Object(r),!0).forEach(function(t){_defineProperty(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):ownKeys(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}var observers=new Map;function makeHydrationObserver(e){if("undefined"==typeof IntersectionObserver)return null;var t=JSON.stringify(e);if(observers.has(t))return observers.get(t);var r=new IntersectionObserver(function(e){e.forEach(function(e){(e.isIntersecting||e.intersectionRatio>0)&&e.target.hydrate&&e.target.hydrate()})},e);return observers.set(t,r),r}function makeHydrationPromise(){var e=function(){},t=new Promise(function(t){e=t});return{hydrate:e,hydrationPromise:t}}var isServer="undefined"==typeof window;function isAsyncComponentFactory(e){return"function"==typeof e}function resolveComponent(e){return isAsyncComponentFactory(e)?e().then(function(e){return e.default}):e}function makeNonce(e){var t=e.component,r=e.hydrate,n=e.hydrationPromise;return function(){return new Promise(function(e){isServer&&r(),n.then(function(){return e(resolveComponent(t))})})}}function makeHydrationBlocker(e,t){return _objectSpread2(_objectSpread2({},t),{},{mixins:[{beforeCreate:function(){this.cleanupHandlers=[];var t=makeHydrationPromise(),r=t.hydrate,n=t.hydrationPromise;this.Nonce=makeNonce({component:e,hydrate:r,hydrationPromise:n}),this.hydrate=r,this.hydrationPromise=n},beforeDestroy:function(){this.cleanup()},mounted:function(){var e=this;if(this.$el.nodeType!==Node.COMMENT_NODE){if(!this.never){if(this.whenVisible){var t=makeHydrationObserver(!0!==this.whenVisible?this.whenVisible:void 0);if(!t)return void this.hydrate();this.$el.hydrate=this.hydrate;return this.hydrationPromise.then(function(){return t.unobserve(e.$el)}),void t.observe(this.$el)}if(this.whenIdle){if(!("requestIdleCallback"in window&&"requestAnimationFrame"in window))return void this.hydrate();var r=requestIdleCallback(function(){requestAnimationFrame(e.hydrate)},{timeout:this.idleTimeout});this.hydrationPromise.then(function(){return cancelIdleCallback(r)})}if(this.interactionEvents.length){var n={capture:!0,once:!0,passive:!0};this.interactionEvents.forEach(function(t){var r,i=[t,e.hydrate,n];(r=e.$el).addEventListener.apply(r,i),e.cleanupHandlers.push(function(){var t;return(t=e.$el).removeEventListener.apply(t,i)})})}}}else this.hydrate()},methods:{cleanup:function(){this.cleanupHandlers.forEach(function(e){return e()})}},render:function(e){return e(this.Nonce,{props:this.$attrs},this.$slots.default)}}]})}function hydrateWhenIdle(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).timeout,r=void 0===t?2e3:t;return makeHydrationBlocker(e,{beforeCreate:function(){this.whenIdle=!0,this.idleTimeout=r}})}function hydrateWhenVisible(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).observerOptions,r=void 0===t?void 0:t;return makeHydrationBlocker(e,{beforeCreate:function(){this.whenVisible=r||!0}})}function hydrateNever(e){return makeHydrationBlocker(e,{beforeCreate:function(){this.never=!0}})}function hydrateOnInteraction(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).event,r=void 0===t?"focus":t,n=Array.isArray(r)?r:[r];return makeHydrationBlocker(e,{beforeCreate:function(){this.interactionEvents=n}})}var Placeholder={render:function(){return this.$slots.default}},LazyHydrate=makeHydrationBlocker(Placeholder,{props:{idleTimeout:{default:2e3,type:Number},never:{type:Boolean},onInteraction:{type:[Array,Boolean,String]},triggerHydration:{default:!1,type:Boolean},whenIdle:{type:Boolean},whenVisible:{type:[Boolean,Object]}},computed:{interactionEvents:function(){return this.onInteraction?!0===this.onInteraction?["focus"]:Array.isArray(this.onInteraction)?this.onInteraction:[this.onInteraction]:[]}},watch:{triggerHydration:{immediate:!0,handler:function(e){e&&this.hydrate()}}}});export default LazyHydrate;export{hydrateNever,hydrateOnInteraction,hydrateWhenIdle,hydrateWhenVisible}; |
@@ -57,3 +57,3 @@ (function (global, factory) { | ||
var observers = new Map(); | ||
function createObserver(options) { | ||
function makeHydrationObserver(options) { | ||
if (typeof IntersectionObserver === "undefined") return null; | ||
@@ -75,162 +75,206 @@ var optionKey = JSON.stringify(options); | ||
} | ||
function loadingComponentFactory(resolvableComponent, options) { | ||
return _objectSpread2({ | ||
render: function render(h) { | ||
var tag = this.$el ? this.$el.tagName : "div"; // eslint-disable-next-line no-underscore-dangle | ||
if (!this.$el) resolvableComponent._resolve(); | ||
return h(tag); | ||
} | ||
}, options); | ||
} | ||
function resolvableComponentFactory(component) { | ||
var resolve; | ||
var promise = new Promise(function (newResolve) { | ||
resolve = newResolve; | ||
}); // eslint-disable-next-line no-underscore-dangle | ||
function makeHydrationPromise() { | ||
var hydrate = function hydrate() {}; | ||
promise._resolve = function () { | ||
resolve(typeof component === "function" ? component() : component); | ||
var hydrationPromise = new Promise(function (resolve) { | ||
hydrate = resolve; | ||
}); | ||
return { | ||
hydrate: hydrate, | ||
hydrationPromise: hydrationPromise | ||
}; | ||
return promise; | ||
} | ||
var isServer = typeof window === "undefined"; | ||
function hydrateWhenIdle(component) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
ignoredProps = _ref.ignoredProps; | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted: function mounted() { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!("requestIdleCallback" in window) || !("requestAnimationFrame" in window)) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
resolvableComponent._resolve(); | ||
function isAsyncComponentFactory(componentOrFactory) { | ||
return typeof componentOrFactory === "function"; | ||
} | ||
return; | ||
} | ||
function resolveComponent(componentOrFactory) { | ||
if (isAsyncComponentFactory(componentOrFactory)) { | ||
return componentOrFactory().then(function (componentModule) { | ||
return componentModule.default; | ||
}); | ||
} | ||
var id = requestIdleCallback(function () { | ||
// eslint-disable-next-line no-underscore-dangle | ||
requestAnimationFrame(resolvableComponent._resolve); | ||
}, { | ||
timeout: this.idleTimeout | ||
}); | ||
return componentOrFactory; | ||
} | ||
var cleanup = function cleanup() { | ||
return cancelIdleCallback(id); | ||
}; | ||
resolvableComponent.then(cleanup); | ||
} | ||
}); | ||
function makeNonce(_ref) { | ||
var component = _ref.component, | ||
hydrate = _ref.hydrate, | ||
hydrationPromise = _ref.hydrationPromise; | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
return new Promise(function (resolve) { | ||
if (isServer) hydrate(); | ||
hydrationPromise.then(function () { | ||
return resolve(resolveComponent(component)); | ||
}); | ||
}); | ||
}; | ||
} | ||
function hydrateWhenVisible(component) { | ||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
ignoredProps = _ref2.ignoredProps, | ||
observerOptions = _ref2.observerOptions; | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var observer = createObserver(observerOptions); | ||
var loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted: function mounted() { | ||
var _this = this; | ||
function makeHydrationBlocker(component, options) { | ||
return _objectSpread2(_objectSpread2({}, options), {}, { | ||
mixins: [{ | ||
beforeCreate: function beforeCreate() { | ||
this.cleanupHandlers = []; | ||
// If Intersection Observer API is not supported, hydrate immediately. | ||
if (!observer) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
resolvableComponent._resolve(); | ||
var _makeHydrationPromise = makeHydrationPromise(), | ||
hydrate = _makeHydrationPromise.hydrate, | ||
hydrationPromise = _makeHydrationPromise.hydrationPromise; | ||
return; | ||
} // eslint-disable-next-line no-underscore-dangle | ||
this.Nonce = makeNonce({ | ||
component: component, | ||
hydrate: hydrate, | ||
hydrationPromise: hydrationPromise | ||
}); | ||
this.hydrate = hydrate; | ||
this.hydrationPromise = hydrationPromise; | ||
}, | ||
beforeDestroy: function beforeDestroy() { | ||
this.cleanup(); | ||
}, | ||
mounted: function mounted() { | ||
var _this = this; | ||
if (this.$el.nodeType === Node.COMMENT_NODE) { | ||
// No SSR rendered content, hydrate immediately. | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = resolvableComponent._resolve; | ||
if (this.never) return; | ||
var cleanup = function cleanup() { | ||
return observer.unobserve(_this.$el); | ||
}; | ||
if (this.whenVisible) { | ||
var observerOptions = this.whenVisible !== true ? this.whenVisible : undefined; | ||
var observer = makeHydrationObserver(observerOptions); | ||
resolvableComponent.then(cleanup); | ||
observer.observe(this.$el); | ||
if (!observer) { | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = this.hydrate; | ||
var cleanup = function cleanup() { | ||
return observer.unobserve(_this.$el); | ||
}; | ||
this.hydrationPromise.then(cleanup); | ||
observer.observe(this.$el); | ||
return; | ||
} | ||
if (this.whenIdle) { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!("requestIdleCallback" in window) || !("requestAnimationFrame" in window)) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
this.hydrate(); | ||
return; | ||
} // @ts-ignore | ||
var id = requestIdleCallback(function () { | ||
// eslint-disable-next-line no-underscore-dangle | ||
requestAnimationFrame(_this.hydrate); | ||
}, { | ||
timeout: this.idleTimeout | ||
}); // @ts-ignore | ||
var _cleanup = function _cleanup() { | ||
return cancelIdleCallback(id); | ||
}; | ||
this.hydrationPromise.then(_cleanup); | ||
} | ||
if (this.interactionEvents.length) { | ||
var eventListenerOptions = { | ||
capture: true, | ||
once: true, | ||
passive: true | ||
}; | ||
this.interactionEvents.forEach(function (eventName) { | ||
var _this$$el; | ||
var eventListenerParams = [eventName, _this.hydrate, eventListenerOptions]; | ||
(_this$$el = _this.$el).addEventListener.apply(_this$$el, eventListenerParams); | ||
_this.cleanupHandlers.push(function () { | ||
var _this$$el2; | ||
return (_this$$el2 = _this.$el).removeEventListener.apply(_this$$el2, eventListenerParams); | ||
}); | ||
}); | ||
} | ||
}, | ||
methods: { | ||
cleanup: function cleanup() { | ||
this.cleanupHandlers.forEach(function (handler) { | ||
return handler(); | ||
}); | ||
} | ||
}, | ||
render: function render(h) { | ||
return h(this.Nonce, { | ||
props: this.$attrs | ||
}, this.$slots.default); | ||
} | ||
}] | ||
}); | ||
} | ||
function hydrateWhenIdle(componentOrFactory) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$timeout = _ref.timeout, | ||
timeout = _ref$timeout === void 0 ? 2000 : _ref$timeout; | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.whenIdle = true; | ||
this.idleTimeout = timeout; | ||
} | ||
}); | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
}; | ||
} | ||
function hydrateNever(component) { | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var loading = loadingComponentFactory(resolvableComponent); | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
}; | ||
function hydrateWhenVisible(componentOrFactory) { | ||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref2$observerOptions = _ref2.observerOptions, | ||
observerOptions = _ref2$observerOptions === void 0 ? undefined : _ref2$observerOptions; | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.whenVisible = observerOptions || true; | ||
} | ||
}); | ||
} | ||
function hydrateOnInteraction(component) { | ||
function hydrateNever(componentOrFactory) { | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.never = true; | ||
} | ||
}); | ||
} | ||
function hydrateOnInteraction(componentOrFactory) { | ||
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref3$event = _ref3.event, | ||
event = _ref3$event === void 0 ? "focus" : _ref3$event, | ||
ignoredProps = _ref3.ignoredProps; | ||
event = _ref3$event === void 0 ? "focus" : _ref3$event; | ||
if (isServer) return component; | ||
var resolvableComponent = resolvableComponentFactory(component); | ||
var events = Array.isArray(event) ? event : [event]; | ||
var loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted: function mounted() { | ||
var _this2 = this; | ||
events.forEach(function (eventName) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
_this2.$el.addEventListener(eventName, resolvableComponent._resolve, { | ||
capture: true, | ||
once: true, | ||
passive: true | ||
}); | ||
}); | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate: function beforeCreate() { | ||
this.interactionEvents = events; | ||
} | ||
}); | ||
return function () { | ||
return { | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading: loading | ||
}; | ||
}; | ||
} | ||
var Nonce = function Nonce() { | ||
return new Promise(function () {}); | ||
}; | ||
var LazyHydrateBlocker = { | ||
functional: true, | ||
render: function render(h, context) { | ||
return context.props.isHydrated ? context.props.content : h(Nonce); | ||
var Placeholder = { | ||
render: function render() { | ||
return this.$slots.default; | ||
} | ||
}; | ||
var LazyHydrate = { | ||
var LazyHydrate = makeHydrationBlocker(Placeholder, { | ||
props: { | ||
@@ -241,8 +285,8 @@ idleTimeout: { | ||
}, | ||
never: { | ||
type: Boolean | ||
}, | ||
onInteraction: { | ||
type: [Array, Boolean, String] | ||
}, | ||
never: { | ||
type: Boolean | ||
}, | ||
triggerHydration: { | ||
@@ -259,15 +303,2 @@ default: false, | ||
}, | ||
data: function data() { | ||
return { | ||
isHydrated: isServer | ||
}; | ||
}, | ||
watch: { | ||
triggerHydration: { | ||
immediate: true, | ||
handler: function handler(hydrate) { | ||
if (hydrate) this.hydrate(); | ||
} | ||
} | ||
}, | ||
computed: { | ||
@@ -280,97 +311,11 @@ interactionEvents: function interactionEvents() { | ||
}, | ||
mounted: function mounted() { | ||
var _this3 = this; | ||
if (this.$el.childElementCount === 0) { | ||
// No SSR rendered content, hydrate immediately. | ||
this.hydrate(); | ||
return; | ||
} | ||
if (this.never) return; | ||
this.interactionEvents.forEach(function (eventName) { | ||
_this3.$el.addEventListener(eventName, _this3.hydrate, { | ||
capture: true, | ||
once: true, | ||
passive: true | ||
}); | ||
}); | ||
if (this.interactionEvents.length) { | ||
this.interaction = function () { | ||
_this3.interactionEvents.forEach(function (eventName) { | ||
return _this3.$el.removeEventListener(eventName, _this3.hydrate); | ||
}); | ||
}; | ||
} | ||
if (this.whenIdle) { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!("requestIdleCallback" in window) || !("requestAnimationFrame" in window)) { | ||
this.hydrate(); | ||
return; | ||
watch: { | ||
triggerHydration: { | ||
immediate: true, | ||
handler: function handler(isTriggered) { | ||
if (isTriggered) this.hydrate(); | ||
} | ||
var id = requestIdleCallback(function () { | ||
requestAnimationFrame(function () { | ||
_this3.hydrate(); | ||
}); | ||
}, { | ||
timeout: this.idleTimeout | ||
}); | ||
this.idle = function () { | ||
return cancelIdleCallback(id); | ||
}; | ||
} | ||
if (this.whenVisible) { | ||
var options = this.whenVisible === true ? {} : this.whenVisible; | ||
var observer = createObserver(options); // If Intersection Observer API is not supported, hydrate immediately. | ||
if (!observer) { | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = this.hydrate; | ||
observer.observe(this.$el); | ||
this.visible = function () { | ||
observer.unobserve(_this3.$el); | ||
delete _this3.$el.hydrate; | ||
}; | ||
} | ||
}, | ||
beforeDestroy: function beforeDestroy() { | ||
this.cleanup(); | ||
}, | ||
methods: { | ||
cleanup: function cleanup() { | ||
var _this4 = this; | ||
var handlers = ["idle", "interaction", "visible"]; | ||
handlers.forEach(function (handler) { | ||
if (handler in _this4) { | ||
_this4[handler](); | ||
delete _this4[handler]; | ||
} | ||
}); | ||
}, | ||
hydrate: function hydrate() { | ||
this.isHydrated = true; | ||
this.cleanup(); | ||
} | ||
}, | ||
render: function render(h) { | ||
return h(LazyHydrateBlocker, { | ||
props: { | ||
content: this.$slots.default, | ||
isHydrated: this.isHydrated | ||
} | ||
}); | ||
} | ||
}; | ||
}); | ||
@@ -377,0 +322,0 @@ exports.default = LazyHydrate; |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["vue-lazy-hydration"]={})}(this,function(e){"use strict";function t(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}var r=new Map;function i(e){if("undefined"==typeof IntersectionObserver)return null;var t=JSON.stringify(e);if(r.has(t))return r.get(t);var n=new IntersectionObserver(function(e){e.forEach(function(e){(e.isIntersecting||e.intersectionRatio>0)&&e.target.hydrate&&e.target.hydrate()})},e);return r.set(t,n),n}function o(e,r){return function(e){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?n(Object(i),!0).forEach(function(n){t(e,n,i[n])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):n(Object(i)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))})}return e}({render:function(t){var n=this.$el?this.$el.tagName:"div";return this.$el||e._resolve(),t(n)}},r)}function a(e){var t,n=new Promise(function(e){t=e});return n._resolve=function(){t("function"==typeof e?e():e)},n}var s="undefined"==typeof window;var u=function(){return new Promise(function(){})},c={functional:!0,render:function(e,t){return t.props.isHydrated?t.props.content:e(u)}},l={props:{idleTimeout:{default:2e3,type:Number},onInteraction:{type:[Array,Boolean,String]},never:{type:Boolean},triggerHydration:{default:!1,type:Boolean},whenIdle:{type:Boolean},whenVisible:{type:[Boolean,Object]}},data:function(){return{isHydrated:s}},watch:{triggerHydration:{immediate:!0,handler:function(e){e&&this.hydrate()}}},computed:{interactionEvents:function(){return this.onInteraction?!0===this.onInteraction?["focus"]:Array.isArray(this.onInteraction)?this.onInteraction:[this.onInteraction]:[]}},mounted:function(){var e=this;if(0!==this.$el.childElementCount){if(!this.never){if(this.interactionEvents.forEach(function(t){e.$el.addEventListener(t,e.hydrate,{capture:!0,once:!0,passive:!0})}),this.interactionEvents.length&&(this.interaction=function(){e.interactionEvents.forEach(function(t){return e.$el.removeEventListener(t,e.hydrate)})}),this.whenIdle){if(!("requestIdleCallback"in window&&"requestAnimationFrame"in window))return void this.hydrate();var t=requestIdleCallback(function(){requestAnimationFrame(function(){e.hydrate()})},{timeout:this.idleTimeout});this.idle=function(){return cancelIdleCallback(t)}}if(this.whenVisible){var n=i(!0===this.whenVisible?{}:this.whenVisible);if(!n)return void this.hydrate();this.$el.hydrate=this.hydrate,n.observe(this.$el),this.visible=function(){n.unobserve(e.$el),delete e.$el.hydrate}}}}else this.hydrate()},beforeDestroy:function(){this.cleanup()},methods:{cleanup:function(){var e=this;["idle","interaction","visible"].forEach(function(t){t in e&&(e[t](),delete e[t])})},hydrate:function(){this.isHydrated=!0,this.cleanup()}},render:function(e){return e(c,{props:{content:this.$slots.default,isHydrated:this.isHydrated}})}};e.default=l,e.hydrateNever=function(e){if(s)return e;var t=a(e),n=o(t);return function(){return{component:t,delay:0,loading:n}}},e.hydrateOnInteraction=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.event,r=void 0===n?"focus":n,i=t.ignoredProps;if(s)return e;var u=a(e),c=Array.isArray(r)?r:[r],l=o(u,{props:i,mounted:function(){var e=this;c.forEach(function(t){e.$el.addEventListener(t,u._resolve,{capture:!0,once:!0,passive:!0})})}});return function(){return{component:u,delay:0,loading:l}}},e.hydrateWhenIdle=function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).ignoredProps;if(s)return e;var n=a(e),r=o(n,{props:t,mounted:function(){if("requestIdleCallback"in window&&"requestAnimationFrame"in window){var e=requestIdleCallback(function(){requestAnimationFrame(n._resolve)},{timeout:this.idleTimeout});n.then(function(){return cancelIdleCallback(e)})}else n._resolve()}});return function(){return{component:n,delay:0,loading:r}}},e.hydrateWhenVisible=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.ignoredProps,r=t.observerOptions;if(s)return e;var u=a(e),c=i(r),l=o(u,{props:n,mounted:function(){var e=this;c?(this.$el.hydrate=u._resolve,u.then(function(){return c.unobserve(e.$el)}),c.observe(this.$el)):u._resolve()}});return function(){return{component:u,delay:0,loading:l}}},Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["vue-lazy-hydration"]={})}(this,function(e){"use strict";function t(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function r(e){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?n(Object(i),!0).forEach(function(n){t(e,n,i[n])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):n(Object(i)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))})}return e}var i=new Map;var o="undefined"==typeof window;function a(e,t){return r(r({},t),{},{mixins:[{beforeCreate:function(){this.cleanupHandlers=[];var t=function(){var e=function(){},t=new Promise(function(t){e=t});return{hydrate:e,hydrationPromise:t}}(),n=t.hydrate,r=t.hydrationPromise;this.Nonce=function(e){var t=e.component,n=e.hydrate,r=e.hydrationPromise;return function(){return new Promise(function(e){o&&n(),r.then(function(){return e(function(e){return"function"==typeof e}(n=t)?n().then(function(e){return e.default}):n);var n})})}}({component:e,hydrate:n,hydrationPromise:r}),this.hydrate=n,this.hydrationPromise=r},beforeDestroy:function(){this.cleanup()},mounted:function(){var e=this;if(this.$el.nodeType!==Node.COMMENT_NODE){if(!this.never){if(this.whenVisible){var t=function(e){if("undefined"==typeof IntersectionObserver)return null;var t=JSON.stringify(e);if(i.has(t))return i.get(t);var n=new IntersectionObserver(function(e){e.forEach(function(e){(e.isIntersecting||e.intersectionRatio>0)&&e.target.hydrate&&e.target.hydrate()})},e);return i.set(t,n),n}(!0!==this.whenVisible?this.whenVisible:void 0);if(!t)return void this.hydrate();this.$el.hydrate=this.hydrate;return this.hydrationPromise.then(function(){return t.unobserve(e.$el)}),void t.observe(this.$el)}if(this.whenIdle){if(!("requestIdleCallback"in window&&"requestAnimationFrame"in window))return void this.hydrate();var n=requestIdleCallback(function(){requestAnimationFrame(e.hydrate)},{timeout:this.idleTimeout});this.hydrationPromise.then(function(){return cancelIdleCallback(n)})}if(this.interactionEvents.length){var r={capture:!0,once:!0,passive:!0};this.interactionEvents.forEach(function(t){var n,i=[t,e.hydrate,r];(n=e.$el).addEventListener.apply(n,i),e.cleanupHandlers.push(function(){var t;return(t=e.$el).removeEventListener.apply(t,i)})})}}}else this.hydrate()},methods:{cleanup:function(){this.cleanupHandlers.forEach(function(e){return e()})}},render:function(e){return e(this.Nonce,{props:this.$attrs},this.$slots.default)}}]})}var s=a({render:function(){return this.$slots.default}},{props:{idleTimeout:{default:2e3,type:Number},never:{type:Boolean},onInteraction:{type:[Array,Boolean,String]},triggerHydration:{default:!1,type:Boolean},whenIdle:{type:Boolean},whenVisible:{type:[Boolean,Object]}},computed:{interactionEvents:function(){return this.onInteraction?!0===this.onInteraction?["focus"]:Array.isArray(this.onInteraction)?this.onInteraction:[this.onInteraction]:[]}},watch:{triggerHydration:{immediate:!0,handler:function(e){e&&this.hydrate()}}}});e.default=s,e.hydrateNever=function(e){return a(e,{beforeCreate:function(){this.never=!0}})},e.hydrateOnInteraction=function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).event,n=void 0===t?"focus":t,r=Array.isArray(n)?n:[n];return a(e,{beforeCreate:function(){this.interactionEvents=r}})},e.hydrateWhenIdle=function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).timeout,n=void 0===t?2e3:t;return a(e,{beforeCreate:function(){this.whenIdle=!0,this.idleTimeout=n}})},e.hydrateWhenVisible=function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).observerOptions,n=void 0===t?void 0:t;return a(e,{beforeCreate:function(){this.whenVisible=n||!0}})},Object.defineProperty(e,"__esModule",{value:!0})}); |
{ | ||
"name": "vue-lazy-hydration", | ||
"version": "2.0.0-alpha.0", | ||
"version": "2.0.0-beta.0", | ||
"description": "Lazy hydration of server-side rendered Vue.js components", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -8,8 +8,6 @@ # vue-lazy-hydration | ||
> Lazy hydration of server-side rendered Vue.js components. | ||
> Lazy Hydration of Server-Side Rendered Vue.js Components | ||
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/O4O7U55Y) | ||
## Motivation | ||
`vue-lazy-hydration` is a renderless Vue.js 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**. | ||
@@ -192,10 +190,7 @@ | ||
), | ||
ArticleContent: hydrateNever( | ||
() => import('./ArticleContent.vue'), | ||
{ ignoredProps: ['content'] }, | ||
), | ||
ArticleContent: hydrateNever(() => import('./ArticleContent.vue')), | ||
CommentForm: hydrateOnInteraction( | ||
() => import('./CommentForm.vue'), | ||
// `focus` is the default event. | ||
{ event: 'focus', ignoredProps: ['articleId'] }, | ||
{ event: 'focus' }, | ||
), | ||
@@ -209,8 +204,2 @@ ImageSlider: hydrateWhenIdle(() => import('./ImageSlider.vue')), | ||
### Caveats | ||
1. Properties passed to a wrapped component are rendered as an HTML attribute on the root element. | ||
E.g. `<ArticleContent :content="article.content"/>` would render to `<div class="ArticleContent" content="Lorem ipsum dolor ...">Lorem ipsum dolor ...</div>` as long as you don't provide `content` as an ignored property the way you can see in the example above. | ||
2. When using `hydrateWhenVisible` and `hydrateOnInteraction` all instances of a certain component are immediately hydrated as soon as one of the instances becomes visible or is interacted with. | ||
## Benchmarks | ||
@@ -230,2 +219,24 @@ | ||
## Upgrade v1.x to v2.x | ||
Breaking changes: | ||
- `ssr-only` was renamed to `never` (as in "Hydrate this? Never!"). | ||
```diff | ||
-<LazyHydrate ssr-only> | ||
+<LazyHydrate never> | ||
<ArticleContent/> | ||
</LazyHydrate> | ||
``` | ||
- Specyfing `ignored-props` on Import Wrappers is not necessary anymore. | ||
```diff | ||
components: { | ||
- ArticleContent: hydrateNever(() => import('./ArticleContent.vue'), { ignoredProps: ['content'] }), | ||
+ ArticleContent: hydrateNever(() => import('./ArticleContent.vue')), | ||
} | ||
``` | ||
## Articles | ||
@@ -232,0 +243,0 @@ |
@@ -1,119 +0,45 @@ | ||
import { | ||
createObserver, | ||
loadingComponentFactory, | ||
resolvableComponentFactory, | ||
} from './utils'; | ||
import { makeHydrationBlocker } from './utils/hydration-blocker'; | ||
const isServer = typeof window === `undefined`; | ||
export function hydrateWhenIdle(component, { ignoredProps } = {}) { | ||
if (isServer) return component; | ||
const resolvableComponent = resolvableComponentFactory(component); | ||
const loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted() { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!(`requestIdleCallback` in window) || !(`requestAnimationFrame` in window)) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
resolvableComponent._resolve(); | ||
return; | ||
} | ||
const id = requestIdleCallback(() => { | ||
// eslint-disable-next-line no-underscore-dangle | ||
requestAnimationFrame(resolvableComponent._resolve); | ||
}, { timeout: this.idleTimeout }); | ||
const cleanup = () => cancelIdleCallback(id); | ||
resolvableComponent.then(cleanup); | ||
export function hydrateWhenIdle(componentOrFactory, { timeout = 2000 } = {}) { | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate() { | ||
this.whenIdle = true; | ||
this.idleTimeout = timeout; | ||
}, | ||
}); | ||
return () => ({ | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading, | ||
}); | ||
} | ||
export function hydrateWhenVisible(component, { ignoredProps, observerOptions } = {}) { | ||
if (isServer) return component; | ||
const resolvableComponent = resolvableComponentFactory(component); | ||
const observer = createObserver(observerOptions); | ||
const loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted() { | ||
// If Intersection Observer API is not supported, hydrate immediately. | ||
if (!observer) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
resolvableComponent._resolve(); | ||
return; | ||
} | ||
// eslint-disable-next-line no-underscore-dangle | ||
this.$el.hydrate = resolvableComponent._resolve; | ||
const cleanup = () => observer.unobserve(this.$el); | ||
resolvableComponent.then(cleanup); | ||
observer.observe(this.$el); | ||
export function hydrateWhenVisible(componentOrFactory, { observerOptions = undefined } = {}) { | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate() { | ||
this.whenVisible = observerOptions || true; | ||
}, | ||
}); | ||
return () => ({ | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading, | ||
}); | ||
} | ||
export function hydrateNever(component) { | ||
if (isServer) return component; | ||
const resolvableComponent = resolvableComponentFactory(component); | ||
const loading = loadingComponentFactory(resolvableComponent); | ||
return () => ({ | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading, | ||
export function hydrateNever(componentOrFactory) { | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate() { | ||
this.never = true; | ||
}, | ||
}); | ||
} | ||
export function hydrateOnInteraction(component, { event = `focus`, ignoredProps } = {}) { | ||
if (isServer) return component; | ||
const resolvableComponent = resolvableComponentFactory(component); | ||
export function hydrateOnInteraction(componentOrFactory, { event = `focus` } = {}) { | ||
const events = Array.isArray(event) ? event : [event]; | ||
const loading = loadingComponentFactory(resolvableComponent, { | ||
props: ignoredProps, | ||
mounted() { | ||
events.forEach((eventName) => { | ||
// eslint-disable-next-line no-underscore-dangle | ||
this.$el.addEventListener(eventName, resolvableComponent._resolve, { | ||
capture: true, | ||
once: true, | ||
passive: true, | ||
}); | ||
}); | ||
return makeHydrationBlocker(componentOrFactory, { | ||
beforeCreate() { | ||
this.interactionEvents = events; | ||
}, | ||
}); | ||
return () => ({ | ||
component: resolvableComponent, | ||
delay: 0, | ||
loading, | ||
}); | ||
} | ||
const Nonce = () => new Promise(() => {}); | ||
const LazyHydrateBlocker = { | ||
functional: true, | ||
render: (h, context) => (context.props.isHydrated ? context.props.content : h(Nonce)), | ||
const Placeholder = { | ||
render() { | ||
return this.$slots.default; | ||
}, | ||
}; | ||
export default { | ||
export default makeHydrationBlocker(Placeholder, { | ||
props: { | ||
@@ -124,8 +50,8 @@ idleTimeout: { | ||
}, | ||
never: { | ||
type: Boolean, | ||
}, | ||
onInteraction: { | ||
type: [Array, Boolean, String], | ||
}, | ||
never: { | ||
type: Boolean, | ||
}, | ||
triggerHydration: { | ||
@@ -142,15 +68,2 @@ default: false, | ||
}, | ||
data() { | ||
return { | ||
isHydrated: isServer, | ||
}; | ||
}, | ||
watch: { | ||
triggerHydration: { | ||
immediate: true, | ||
handler(hydrate) { | ||
if (hydrate) this.hydrate(); | ||
}, | ||
}, | ||
}, | ||
computed: { | ||
@@ -166,88 +79,10 @@ interactionEvents() { | ||
}, | ||
mounted() { | ||
if (this.$el.childElementCount === 0) { | ||
// No SSR rendered content, hydrate immediately. | ||
this.hydrate(); | ||
return; | ||
} | ||
if (this.never) return; | ||
this.interactionEvents.forEach((eventName) => { | ||
this.$el.addEventListener(eventName, this.hydrate, { | ||
capture: true, | ||
once: true, | ||
passive: true, | ||
}); | ||
}); | ||
if (this.interactionEvents.length) { | ||
this.interaction = () => { | ||
this.interactionEvents.forEach( | ||
eventName => this.$el.removeEventListener(eventName, this.hydrate), | ||
); | ||
}; | ||
} | ||
if (this.whenIdle) { | ||
// If `requestIdleCallback()` or `requestAnimationFrame()` | ||
// is not supported, hydrate immediately. | ||
if (!(`requestIdleCallback` in window) || !(`requestAnimationFrame` in window)) { | ||
this.hydrate(); | ||
return; | ||
} | ||
const id = requestIdleCallback(() => { | ||
requestAnimationFrame(() => { | ||
this.hydrate(); | ||
}); | ||
}, { timeout: this.idleTimeout }); | ||
this.idle = () => cancelIdleCallback(id); | ||
} | ||
if (this.whenVisible) { | ||
const options = this.whenVisible === true ? {} : this.whenVisible; | ||
const observer = createObserver(options); | ||
// If Intersection Observer API is not supported, hydrate immediately. | ||
if (!observer) { | ||
this.hydrate(); | ||
return; | ||
} | ||
this.$el.hydrate = this.hydrate; | ||
observer.observe(this.$el); | ||
this.visible = () => { | ||
observer.unobserve(this.$el); | ||
delete this.$el.hydrate; | ||
}; | ||
} | ||
}, | ||
beforeDestroy() { | ||
this.cleanup(); | ||
}, | ||
methods: { | ||
cleanup() { | ||
const handlers = [`idle`, `interaction`, `visible`]; | ||
handlers.forEach((handler) => { | ||
if (handler in this) { | ||
this[handler](); | ||
delete this[handler]; | ||
} | ||
}); | ||
watch: { | ||
triggerHydration: { | ||
immediate: true, | ||
handler(isTriggered) { | ||
if (isTriggered) this.hydrate(); | ||
}, | ||
}, | ||
hydrate() { | ||
this.isHydrated = true; | ||
this.cleanup(); | ||
}, | ||
}, | ||
render(h) { | ||
return h(LazyHydrateBlocker, { | ||
props: { | ||
content: this.$slots.default, | ||
isHydrated: this.isHydrated, | ||
}, | ||
}); | ||
}, | ||
}; | ||
}); |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
12
282
47528
753
1