Lazy hydration of server-side rendered Vue.js components.
This plugin is currently in an early beta stage. Although everything seems to work fine in combination with the applications I tested it with, there might be certain cases I haven't considered. I'd be very happy if you test it with your own projects and report back if it works for you.
Use at your own risk.
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.
npm install vue-lazy-hydrationimport LazyHydrate from 'vue-lazy-hydration'; // ... export default { // ... components: { LazyHydrate, // ... }, // ... };In the example below you can see the four hydration modes in action.
<template> <div class="ArticlePage"> <LazyHydrate when-idle> <ImageSlider/> </LazyHydrate> <LazyHydrate ssr-only> <ArticleContent :content="article.content"/> </LazyHydrate> <LazyHydrate when-visible> <AdSlider/> </LazyHydrate> <!-- `on-interaction` listens for a `focus` event by default ... --> <LazyHydrate on-interaction> <CommentForm :article-id="article.id"/> </LazyHydrate> <!-- ... but you can listen for any event you want ... --> <LazyHydrate on-interaction="click"> <CommentForm :article-id="article.id"/> </LazyHydrate> <!-- ... or even multiple events. --> <LazyHydrate :on-interaction="['click', 'touchstart']"> <CommentForm :article-id="article.id"/> </LazyHydrate> </div> </template> <script> import LazyHydrate from 'vue-lazy-hydration'; export default { components: { LazyHydrate, AdSlider: () => import('./AdSlider.vue'), ArticleContent: () => import('./ArticleContent.vue'), CommentForm: () => import('./CommentForm.vue'), ImageSlider: () => import('./ImageSlider.vue'), }, // ... }; </script>- Because it is at the very top of the page, the
ImageSlidershould be hydrated eventually, but we can wait until the browser is idle. - The
ArticleContentcomponent 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). - Next we can see the
AdSliderbeneath the article content, this component will most likely not be visible initially so we can delay hydration until the point it becomes visible. - At the very bottom of the page we want to render a
CommentFormbut 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.
<template> <div class="ArticlePage"> <LazyHydrate on-interaction> <CommentForm slot-scope="{ hydrated }" v-if="hydrated" :article-id="article.id" /> </LazyHydrate> </div> </template> <script> import LazyHydrate from 'vue-lazy-hydration'; export default { components: { LazyHydrate, // The `CommentForm` is only imported if `hydrated` is true. CommentForm: () => import('./CommentForm.vue'), }, // ... }; </script>Sometimes you might want to prevent a component from loading initially but you want to activate it on demand if a certain action is triggered. You can do this by manually triggering the component to hydrate like you can see in the following example.
<template> <div class="MyComponent"> <button @click="editModeActive = true"> Activate edit mode </button> <LazyHydrate ssr-only :trigger-hydration="editModeActive"> <UserSettingsForm/> </LazyHydrate> </div> </template> <script> import LazyHydrate from 'vue-lazy-hydration'; export default { components: { LazyHydrate, UserSettingsForm: () => import('./UserSettingsForm.vue'), }, data() { return { editModeActive: false, }; }, // ... }; </script>Because of how this package works, it is not possible to nest multiple root nodes inside of a single <LazyHydrate>. But you can wrap multiple components with a <div>.
<template> <div class="MyComponent"> <LazyHydrate ssr-only> <div> <ArticleHeader/> <ArticleContent/> <ArticleMetaInfo/> <ArticleFooter/> </div> </LazyHydrate> </div> </template>This plugin will not work as advertised if you're not using it in combination with SSR. Although it should work with every pre-rendering approach (like Prerender SPA Plugin, Gridsome, ...) I've only tested it with Nuxt.js so far.
- How to Drastically Reduce Estimated Input Latency and Time to Interactive of SSR Vue.js Applications
The code of the current implementation of this package is based on a similar package created by Rahul Kadyan. Thanks to his code I'm finally able to build a clean solution for what I dreamed of when I created the abomination.
Markus Oberlehner
Website: https://markus.oberlehner.net
Twitter: https://twitter.com/MaOberlehner
PayPal.me: https://paypal.me/maoberlehner
Patreon: https://www.patreon.com/maoberlehner
MIT