npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nuxt-lazy-hydration

v0.0.1

Published

Lazy hydration of server-side rendered Vue.js components

Downloads

409

Readme

vue-lazy-hydration

Patreon Donate Build Status GitHub stars

Lazy hydration of server-side rendered Vue.js components.

ko-fi

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.

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'),
      { ignoredProps: ['content'] },
    ),
    CommentForm: hydrateOnInteraction(
      () => import('./CommentForm.vue'),
      // `focus` is the default event.
      { event: 'focus', ignoredProps: ['articleId'] },
    ),
    ImageSlider: hydrateWhenIdle(() => import('./ImageSlider.vue')),
  },
  // ...
};
</script>

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

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.

Articles

Credits

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.

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