UNPKG

@vercube/vue-lazy-hydration

Version:

Optimize Vue 3 SSR with lazy hydration, delaying HTML hydration until it's needed for better performance

130 lines (124 loc) 4.25 kB
import { hydrateOnIdle, hydrateOnVisible, hydrateOnInteraction, hydrateOnMediaQuery, defineAsyncComponent, defineComponent, h, markRaw } from 'vue'; /** * Get the hydration strategy based on the hydration properties. * * @param {IHydrationProps} props - The hydration properties. * @returns {HydrationStrategy} The hydration strategy. */ function getHydrationStrategy(props) { // Hydrate on idle strategy // This is useful for components that should only be hydrated when the user is idle // e.g. when the user is not interacting with the component if (props.whenIdle) { return hydrateOnIdle(props.idleTimeout); } // Hydrate on visible strategy // This is useful for components that should only be hydrated when they become visible // e.g. when the user scrolls to the component if (props.whenVisible) { return hydrateOnVisible(typeof props.whenVisible !== 'boolean' ? props.whenVisible : undefined); } // Hydrate on interaction strategy // This is useful for components that should only be hydrated when the user interacts with them // e.g. when the user hovers over the component if (props.onInteraction) { return hydrateOnInteraction(Array.isArray(props.onInteraction) ? props.onInteraction : [props.onInteraction]); } // SSR only hydration strategy // This is useful for components that should only be hydrated on the server // and not on the client if (props.ssrOnly) { return hydrateOnMediaQuery('width >= 999999999999999px'); } // Default hydration strategy return (hydrate, forEach) => () => { forEach(hydrate); }; } /** * Creates a hydration blocker component. * @param {VNode} slot - The element to render. * @returns {} The async component definition. */ function createHydrationBlocker(slot, props) { return defineAsyncComponent({ loader: () => new Promise(resolve => resolve(slot)), hydrate: getHydrationStrategy(props) }); } /** * This component allows to defer DOM hydration by vue. Whatever is wrapped with this component * will not be hydrated when front is loaded, instead it will be deferred. * * We have to override lazy-hydration-wrapper to backport the feature to Vue 2. * We also have to use `defineComponent` instead of Factory to use `setup` function. * `setup` is not available in Factory system, because the instance of class is created on BeforeCreate hook, * that executes after `setup` is called. * * This components is implementation of Vue Core functionality - hydration * Hydration management is available from Vue >= 3.5 * @see: https://github.com/vuejs/core/pull/11458 */ const LazyHydrationComponent = defineComponent({ name: 'CubeLazyHydration', /** Disable inherit attributes */ inheritAttrs: false, /** Define Emits */ emits: ['hydrated'], /** Define props */ props: { triggerHydration: { type: Boolean, required: false }, ssrOnly: { type: Boolean, default: false, required: false }, whenVisible: { type: [Boolean, Object], required: false }, whenIdle: { type: Boolean, required: false }, idleTimeout: { type: Number, default: 2000, required: false }, onInteraction: { type: [Array, String], required: false } }, render() { const slotDefault = this?.$slots?.default?.()?.[0]; if (!slotDefault) { throw new Error('CubeLazyHydration component must have a single root element'); } if (this.$props.triggerHydration) { return h(slotDefault); } const comp = createHydrationBlocker(slotDefault, this.$props); return h(comp); } }); const LazyHydration = markRaw(LazyHydrationComponent); /** * A composable function that returns a lazy hydrated Vue component. * * @template T - The type of the component. * @param {AsyncComponentLoader<T>} loader - The async component loader function. * @param {IHydrationProps} props - The hydration properties. * @returns {ReturnType<typeof defineAsyncComponent>} The lazy hydrated component. */ function useLazyHydration(loader, props) { return defineAsyncComponent({ loader, hydrate: getHydrationStrategy(props) }); } export { LazyHydration, useLazyHydration };