All Projects → maoberlehner → Vue Lazy Hydration

maoberlehner / Vue Lazy Hydration

Licence: mit
Lazy Hydration of Server-Side Rendered Vue.js Components

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Vue Lazy Hydration

Doccano
Open source annotation tool for machine learning practitioners.
Stars: ✭ 5,600 (+602.63%)
Mutual labels:  nuxt, nuxtjs
Vercel Builder
Vercel Builder for Nuxt.js
Stars: ✭ 437 (-45.17%)
Mutual labels:  nuxt, nuxtjs
Awesome Nuxt
A curated list of awesome things related to Nuxt.js
Stars: ✭ 4,285 (+437.64%)
Mutual labels:  nuxt, nuxtjs
Vuecnodejs
⚽️🎉Vue初/中级项目,CnodeJS社区重构。( a junior project of Vue.js, rewrite cnodejs.org ) 预览(DEMO):
Stars: ✭ 705 (-11.54%)
Mutual labels:  nuxt, nuxtjs
Tailwindcss Module
Tailwind CSS module for Nuxt
Stars: ✭ 678 (-14.93%)
Mutual labels:  nuxt, nuxtjs
Wemake Vue Template
Bleeding edge vue template focused on code quality and developer happiness.
Stars: ✭ 645 (-19.07%)
Mutual labels:  nuxt, nuxtjs
Style Resources Module
Nobody likes extra @import statements!
Stars: ✭ 485 (-39.15%)
Mutual labels:  nuxt, nuxtjs
Blog Front
blog-front @nuxt
Stars: ✭ 305 (-61.73%)
Mutual labels:  nuxt, nuxtjs
Wuxt
Nuxt/WordPress development environment, combining the worlds biggest CMS with the most awesome front-end application framework yet.
Stars: ✭ 459 (-42.41%)
Mutual labels:  nuxt, nuxtjs
Composition Api
Composition API hooks for Nuxt.
Stars: ✭ 441 (-44.67%)
Mutual labels:  nuxt, nuxtjs
Nuxt Purgecss
Drop superfluous CSS! A neat PurgeCSS wrapper for Nuxt.js
Stars: ✭ 356 (-55.33%)
Mutual labels:  nuxt, nuxtjs
Sitemap Module
Sitemap Module for Nuxt
Stars: ✭ 539 (-32.37%)
Mutual labels:  nuxt, nuxtjs
Vue Notion
A fast Vue renderer for Notion pages
Stars: ✭ 343 (-56.96%)
Mutual labels:  nuxt, nuxtjs
Firebase Module
🔥 Easily integrate Firebase into your Nuxt project. 🔥
Stars: ✭ 493 (-38.14%)
Mutual labels:  nuxt, nuxtjs
Nuxt Material Admin
Vue-CLI Boilerplate based on Nuxt and vue-material-admin template.
Stars: ✭ 310 (-61.1%)
Mutual labels:  nuxt, nuxtjs
Vue Gallery
📷 Responsive and customizable image and video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers.
Stars: ✭ 405 (-49.18%)
Mutual labels:  nuxt, nuxtjs
Nuxt7
📱 Full Featured iOS & Android PWA Apps with Nuxt.js and Framework7
Stars: ✭ 282 (-64.62%)
Mutual labels:  nuxt, nuxtjs
Graphcms Examples
Example projects to help you get started with GraphCMS
Stars: ✭ 295 (-62.99%)
Mutual labels:  nuxt, nuxtjs
Device Module
Nuxt.js module for detecting device type.
Stars: ✭ 436 (-45.29%)
Mutual labels:  nuxt, nuxtjs
Nuxt Firebase Sns Example
Nuxt v2 & Firebase(Hosting / Functions SSR / Firestore), Google Auth SNS Example.
Stars: ✭ 485 (-39.15%)
Mutual labels:  nuxt, nuxtjs

vue-lazy-hydration

Patreon Donate Build Status GitHub stars

Lazy Hydration of Server-Side Rendered Vue.js Components

ko-fi

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.

Install

npm install vue-lazy-hydration
import LazyHydrate from 'vue-lazy-hydration';
// ...

export default {
  // ...
  components: {
    LazyHydrate,
    // ...
  },
  // ...
};

Basic example

In the example below you can see the four hydration modes in action.

<template>
  <div class="ArticlePage">
    <LazyHydrate when-idle>
      <ImageSlider/>
    </LazyHydrate>

    <LazyHydrate never>
      <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>
  1. Because it is at the very top of the page, the ImageSlider should be hydrated eventually, but we can wait until the browser is idle.
  2. The ArticleContent component is never hydrated on the client, which also means it will never be interactive (static content only).
  3. Next we can see the AdSlider beneath the article content, this component will most likely not be visible initially so we can delay hydration until the point it becomes visible.
  4. 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.

Advanced

Manually trigger hydration

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 never :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>

Multiple root nodes

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 never>
      <div>
        <ArticleHeader/>
        <ArticleContent/>
        <ArticleMetaInfo/>
        <ArticleFooter/>
      </div>
    </LazyHydrate>
  </div>
</template>

Intersection Observer options

Internally the Intersection Observer API is used to determine if a component is visible or not. You can provide Intersection Observer options to the when-visible property to configure the Intersection Observer.

<template>
  <div class="MyComponent">
    <LazyHydrate :when-visible="{ rootMargin: '100px' }">
      <ArticleFooter/>
    </LazyHydrate>
  </div>
</template>

For a list of possible options please take a look at the Intersection Observer API documentation on MDN.

Import Wrappers

Additionally to the <LazyHydrate> wrapper component you can also use Import Wrappers to lazy load and hydrate certain components.

<template>
  <div class="ArticlePage">
    <ImageSlider/>
    <ArticleContent :content="article.content"/>
    <AdSlider/>
    <CommentForm :article-id="article.id"/>
  </div>
</template>

<script>
import {
  hydrateOnInteraction,
  hydrateNever,
  hydrateWhenIdle,
  hydrateWhenVisible,
} from 'vue-lazy-hydration';

export default {
  components: {
    AdSlider: hydrateWhenVisible(
      () => import('./AdSlider.vue'),
      // Optional.
      { observerOptions: { rootMargin: '100px' } },
    ),
    ArticleContent: hydrateNever(() => import('./ArticleContent.vue')),
    CommentForm: hydrateOnInteraction(
      () => import('./CommentForm.vue'),
      // `focus` is the default event.
      { event: 'focus' },
    ),
    ImageSlider: hydrateWhenIdle(() => import('./ImageSlider.vue')),
  },
  // ...
};
</script>

Benchmarks

Without lazy hydration

Without lazy hydration.

With lazy hydration

With lazy hydration.

Caveats

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.

Upgrade v1.x to v2.x

Breaking changes:

  • ssr-only was renamed to never (as in "Hydrate this? Never!").
-<LazyHydrate ssr-only>
+<LazyHydrate never>
   <ArticleContent/>
 </LazyHydrate>
  • Specyfing ignored-props on Import Wrappers is not necessary anymore.
 components: {
-  ArticleContent: hydrateNever(() => import('./ArticleContent.vue'), { ignoredProps: ['content'] }),
+  ArticleContent: hydrateNever(() => import('./ArticleContent.vue')),
 }

Articles

Credits

The code of the v1 version of this package was based on a similar package created by Rahul Kadyan.

Testing

Because the core functionality of vue-lazy-hydration heavily relies on browser APIs like IntersectionObserver and requestIdleCallback(), it is tough to write meaningful unit tests without having to write numerous mocks. Because of that, we mostly use integration tests and some performance benchmarks to test the functionality of this package.

Integration tests

Execute the following commands to run the integration tests:

npm run test:integration:build
npm run test:integration

Performance tests

Execute the following commands to run the performance benchmark:

npm run test:perf:build
npm run test:perf

About

Author

Markus Oberlehner
Website: https://markus.oberlehner.net
Twitter: https://twitter.com/MaOberlehner
PayPal.me: https://paypal.me/maoberlehner
Patreon: https://www.patreon.com/maoberlehner

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].