vue3-lazy-hydration
Lazy Hydration of Server-Side Rendered Vue.js v3 Components
Inspired by vue-lazy-hydration
, this library brings a renderless component, composables and import wrappers to delay the hydration of pre-rendered HTML.
Installation
Use the package manager yarn v1 or npm to install vue3-lazy-hydration.
yarn add vue3-lazy-hydration
npm install vue3-lazy-hydration
Optionally make the renderless component available globally.
import { createSSRApp } from 'vue';
import { LazyHydrationWrapper } from 'vue3-lazy-hydration';
const app = createSSRApp({});
app.component(
'LazyHydrate',
LazyHydrationWrapper
);
Usage
Renderless Component
-
Never hydrate.
<template>
<LazyHydrationWrapper @hydrated="onHydrated">
</LazyHydrationWrapper>
</template>
<script setup>
import { LazyHydrationWrapper } from 'vue3-lazy-hydration';
function onHydrated() {
console.log('this function will never be called !');
}
</script>
-
Delays hydration until the browser is idle.
<template>
<LazyHydrationWrapper :when-idle="4000" @hydrated="onHydrated">
</LazyHydrationWrapper>
</template>
<script setup>
import { LazyHydrationWrapper } from 'vue3-lazy-hydration';
function onHydrated() {
console.log('content hydrated !');
}
</script>
-
Delays hydration until one of the root elements is visible.
<template>
<LazyHydrationWrapper
:when-visible="{ rootMargin: '50px' }"
@hydrated="onHydrated"
>
</LazyHydrationWrapper>
</template>
<script setup>
import { LazyHydrationWrapper } from 'vue3-lazy-hydration';
function onHydrated() {
console.log('content hydrated !');
}
</script>
-
Delays hydration until one of the elements triggers a DOM event (focus by default).
<template>
<LazyHydrationWrapper
:on-interaction="['click', 'touchstart']"
@hydrated="onHydrated"
>
</LazyHydrationWrapper>
</template>
<script setup>
import { LazyHydrationWrapper } from 'vue3-lazy-hydration';
function onHydrated() {
console.log('content hydrated !');
}
</script>
-
Delays hydration until manually triggered.
<template>
<button @click="triggerHydration">Trigger hydration</button>
<LazyHydrationWrapper :when-triggered="triggered" @hydrated="onHydrated">
</LazyHydrationWrapper>
</template>
<script setup>
import { ref } from 'vue';
import { LazyHydrationWrapper } from 'vue3-lazy-hydration';
const triggered = ref(false);
function triggerHydration() {
triggered.value = true;
}
function onHydrated() {
console.log('content hydrated !');
}
</script>
Props declaration
props: {
whenIdle: {
default: false,
type: [Boolean, Number],
},
whenVisible: {
default: false,
type: [Boolean, Object],
},
onInteraction: {
default: false,
type: [Array, Boolean, String],
},
whenTriggered: {
default: undefined,
type: [Boolean, Object],
},
}
Composables
useLazyHydration()
<script setup>
import { useLazyHydration } from 'vue3-lazy-hydration';
const { willPerformHydration, hydrate, onHydrated, onCleanup } =
useLazyHydration();
if (willPerformHydration === false) {
return;
}
onHydrated(() => {
console.log('content hydrated !');
});
onCleanup(() => {
console.log('clean side effects (timeout, listeners, etc...)');
});
hydrate();
</script>
useHydrateWhenIdle({ willPerformHydration, hydrate, onCleanup }, timeout = 2000)
<script setup>
import { useLazyHydration, useHydrateWhenIdle } from 'vue3-lazy-hydration';
const { willPerformHydration, hydrate, onCleanup } = useLazyHydration();
useHydrateWhenIdle({ willPerformHydration, hydrate, onCleanup }, 4000);
</script>
useHydrateWhenVisible({ hydrate, onCleanup }, observerOpts = {})
<script setup>
import { useLazyHydration, useHydrateWhenVisible } from 'vue3-lazy-hydration';
const { willPerformHydration, hydrate, onCleanup } = useLazyHydration();
const observerOptions = {
rootMargin: '0px',
threshold: 1.0,
};
useHydrateWhenVisible(
{ willPerformHydration, hydrate, onCleanup },
observerOptions
);
</script>
useHydrateOnInteraction({ hydrate, onCleanup }, events = ['focus'])
<script setup>
import { useLazyHydration, useHydrateOnInteraction } from 'vue3-lazy-hydration';
const { willPerformHydration, hydrate, onCleanup } = useLazyHydration();
useHydrateOnInteraction({ willPerformHydration, hydrate, onCleanup }, ['focus' 'click']);
</script>
useHydrateWhenTriggered({ willPerformHydration, hydrate, onCleanup }, trigger)
<script setup>
import { toRef } from 'vue';
import {
useLazyHydration,
useHydrateWhenTriggered,
} from 'vue3-lazy-hydration';
const props = defineProps({
triggerHydration: {
default: undefined,
type: [Boolean, Object],
},
});
const result = useLazyHydration();
useHydrateWhenTriggered(result, toRef(props, 'triggerHydration'));
</script>
Import Wrappers
hydrateNever(source)
Wrap a component in a renderless component that will never be hydrated.
<script setup>
import { resolveComponent } from 'vue';
import { hydrateNever } from 'vue3-lazy-hydration';
const NeverHydratedComp = hydrateNever(resolveComponent('ComponentA'));
const NeverHydratedAsyncComp = hydrateNever(() => import('./ComponentB.vue'));
</script>
<template>
<NeverHydratedComp />
<NeverHydratedAsyncComp />
</template>
hydrateWhenIdle(source, timeout = 2000)
Wrap a component in a renderless component that will be hydrated when browser is idle.
<script setup>
import { resolveComponent } from 'vue';
import { hydrateWhenIdle } from 'vue3-lazy-hydration';
const LazilyHydratedComp = hydrateWhenIdle(
resolveComponent('ComponentA'),
2000
);
const LazilyHydratedAsyncComp = hydrateWhenIdle(
() => import('./ComponentB.vue'),
4000
);
</script>
<template>
<LazilyHydratedComp />
<LazilyHydratedAsyncComp />
</template>
hydrateWhenVisible(source, observerOpts = {})
Wrap a component in a renderless component that will be hydrated when one of the root elements is visible.
<script setup>
import { resolveComponent } from 'vue';
import { hydrateWhenVisible } from 'vue3-lazy-hydration';
const LazilyHydratedComp = hydrateWhenVisible(
resolveComponent('ComponentA'),
{ rootMargin: '50px' }
);
const LazilyHydratedAsyncComp = hydrateWhenVisible(
() => import('./ComponentB.vue'),
{
threshold: [0, 0.25, 0.5, 0.75, 1],
}
);
</script>
<template>
<LazilyHydratedComp />
<LazilyHydratedAsyncComp />
</template>
hydrateOnInteraction(source, events = ['focus'])
Wrap a component in a renderless component that will be hydrated when one of the elements trigger one of the events in the events
parameter.
<script setup>
import { resolveComponent } from 'vue';
import { hydrateOnInteraction } from 'vue3-lazy-hydration';
const LazilyHydratedComp = hydrateOnInteraction(
resolveComponent('ComponentA')
);
const LazilyHydratedAsyncComp = hydrateOnInteraction(
() => import('./ComponentB.vue'),
['focus', 'click', 'touchstart']
);
</script>
<template>
<LazilyHydratedComp />
<LazilyHydratedAsyncComp />
</template>
hydrateWhenTriggered(source, trigger)
Wrap a component in a renderless component that will be hydrated when the trigger
parameter changes to true.
<script setup>
import { ref, resolveComponent } from 'vue';
import { hydrateOnInteraction } from 'vue3-lazy-hydration';
const hydrationTriggered = ref(false);
const LazilyHydratedComp = hydrateOnInteraction(
resolveComponent('ComponentA'),
hydrationTriggered
);
const LazilyHydratedAsyncComp = hydrateOnInteraction(
() => import('./ComponentB.vue'),
hydrationTriggered
);
function triggerHydration() => {
hydrationTriggered.value = true
}
</script>
<template>
<button @click="triggerHydration">Trigger</button>
<LazilyHydratedComp />
<LazilyHydratedAsyncComp />
</template>
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update unit tests as appropriate.
Development
-
Clone the repository
git clone https://github.com/freddy38510/vue3-lazy-hydration.git
cd vue3-lazy-hydration
-
Install dependencies
yarn
npm install
-
Start the development server which hosts a demo application to help develop the library
yarn dev
npm run dev
Credits
Many thanks to Markus Oberlehner, the author of the package
vue-lazy-hydration.
License
MIT