Git Product home page Git Product logo

vue-lazy-hydration's Introduction

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

vue-lazy-hydration's People

Contributors

asennoussi avatar danielkellyio avatar dependabot[bot] avatar maoberlehner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-lazy-hydration's Issues

add rootMargin config for IntersectionObserver to make possible hydration a bit before item enters viewport

thanks so much for your comprehensive work.

https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin

for example if I were to set rootMargin to 20% the item would be hydrated 20% of viewport height before the item enters the viewport.

this can be useful to lazy load images a bit before they're supposed to be shown to reduce load flashes.

also, iOS safari's disappearing browser chrome (e.g. the bottom menu bar) can lead to unexpected intersection calculations, and in my app some elements at the bottom of the screen would never get hydrated unless I extend the rootMargin.

Optimize auto hydration on re-render logic

Currently, components are automatically hydrated if a re-render is triggered (because a property changes for example). There are two problems with this:

  1. This might not be expected by people using this package?
  2. It is not implemented very elegantly - in order to skip initial render, a timeout is used. But if a (manual) re-render is triggered before the timeout, nothing happens.

Cannot read property '0' of undefined

Hello just found this npm package and i wanted to test but unfortunately i cant use it from strange error that it comes from LazyHydrate.js:339:31

<LazyHydrate when-visible>
         <post-list v-if="posts && !isLoading" :posts="posts" title="Recent Posts"></post-list>
       </LazyHydrate>

And it gives me error Cannot read property '0' of undefined the error cames from here

render: function render(h) {
      var tag = this.$el ? this.$el.tagName : "div";
      var child = this.$scopedSlots.default ? this.$scopedSlots.default({
        hydrated: this.hydrated
      }) : this.$slots.default[0]; // This line causes error
      var vnode = this.hydrated ? child : h(tag); // Special thanks to Rahul Kadyan for the following lines of code.
      // https://github.com/znck

Nuxt.js support

Hey,

What version of NUXT are supported?

When using Nuxt.js v2.13.3, on universal mode with SSR, the components are still loaded on the client with JS

Thanks,
Lewis

ignoredProps does not work when using hydrateSsrOnly

ignoredProps does not work when using hydrateSsrOnl

  • tested with with NuxtJS ssr mode
  • happens in dev and production
  • 1.0.0-beta.14 was used

code

<template>
  <div>
    <ContentArticles :limit="9" />
    <hr />
    <ContentTodos :limit="50" class="mt-5" />
  </div>
</template>

<script>
import { hydrateSsrOnly, hydrateWhenVisible } from 'vue-lazy-hydration'

export default {
  components: {
    ContentArticles: hydrateSsrOnly(
      () => import('~/components/content/articles.vue'),
      { ignoredProps: ['limit'] }
    ),
    ContentTodos: hydrateWhenVisible(() =>
      import('~/components/content/todos.vue')
    )
  }
}
</script>

still generates a limit attribute for ContentArticles

<div data-fetch-key="0" class="grid grid-cols-3 gap-5" limit="9">[....]</div>

see
https://github.com/lautr/nuxt-performance-starter

to reproduce

  1. checkout repository
  2. yarn install
  3. yarn dev

ssr-only mode and optimizing Vuex INITIAL_STATE [Question]

For ssr-only, like the "Article" in your example, is there any recommended way to avoid serializing the state from Vuex in INITIAL_STATE, since it will never be needed/hydrated client-side?

The Vue SSR guide requires Component's serverPrefetch (similar to Nuxt's asyncData) to store data in Vuex ( https://ssr.vuejs.org/guide/data.html ).

Great library BTW, I am pushing this to a high-traffic production website soon.

Why components always hydrated on initial time (NuxtJS)

<LazyHydrate :trigger-hydration="false" v-slot="{hydrated}">
	<div>
		<div>
			Hi there! {{hydrated}}
		</div>
	</div>
</LazyHydrate>

In this case, "hydrated" always returns true, but I need add condition for hydrating.
Previously, it was working properly in my project. What is the problem?

Using dynamic components

Hello,
It is possible to use with dynamic components?

I have this vue component (available on pages in nuxt folder structure):

and in template tag

_

_

`<script>
import LazyHydrate from 'vue-lazy-hydration';

export default {
    data() {
        return {
            components: {},
        }
    },
    components: {
        LazyHydrate,
        Component: () => import('~/components/Component.vue'),
        ComponentS: () => import('~/components/ComponentS.vue'),
        ComponentT: () => import('~/components/ComponentS.vue'),
    },
    asyncData({$axios, error}) {
        return $axios.$get('URL')
            .then(({data}) => {
                return {
                    components: data.components,
                }
            })
            .catch((e) => {
                //
            })
    }
}

</script>`

I want to set when-idle for the first component and when-visible for the others (i think is better in this way), but i don't know how can i do that.
If i choose to use <LazyHydrate when-visible> and
<component :is="component .name" :key="component .name" slot-scope="{ hydrated }" v-if="hydrated" ></component>
I get error: Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'

Component is loaded without scroll

Why this code, the component foo is loaded without scroll?

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>

    <br v-for="x in 80" :key="x">

    <LazyHydrate when-visible :trigger-hydration="documentLoaded">
      <foo />
    </LazyHydrate>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import LazyHydrate from 'vue-lazy-hydration'

export default {
  name: 'app',
  data() {
    return {
      documentLoaded: false,
    }
  },
  components: {
    HelloWorld,
    LazyHydrate,
    Foo: () => import(/* webpackChunkName: "component-foo" */ './components/Foo.vue'),
  }
}
</script>

Console:

image

Conditional lazy hydration

It would be cool if you could have a parameter to set if you want to "disable" lazy hydration. E.g.:

<LazyHydrate :enabled="false">

I work with dynamic user defined components, and sometimes I'd like to disable LazyHydrate if a certain condition is met (e.g. "disable_lazy_load").

Nested hydration

I doesnt appear nested hydration works, is something like this possible?

<Hydrate when-idle>
   <MyHeader />
</Hydrate>

then in MyHeader

<div>
  <Hydrate on-interaction>
     <MainNav></MainNav>
  </Hydrate>
</div>

The expected behaviour would be Header hydrate on-load when idle, header - navigation hydrates on interaction.

Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'.

Recently got two error messages on sentry. Both are from an Android 4.4.4 device on a Chrome browser (Chrome 73.0.3683)

Not sure if I can help in any way or if they are related (let me know if it's better to create a separate issue for the second one).
1)

TypeError: Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'.
  at call(./node_modules/vue-lazy-hydration/dist/LazyHydrate.esm.js:364:1)
  at invokeWithErrorHandling(./node_modules/vue/dist/vue.runtime.esm.js:1854:1)
  at callHook(./node_modules/vue/dist/vue.runtime.esm.js:4213:1)
  at insert(./node_modules/vue/dist/vue.runtime.esm.js:3139:1)
  at invokeInsertHook(./node_modules/vue/dist/vue.runtime.esm.js:6340:1)
  at __patch__(./node_modules/vue/dist/vue.runtime.esm.js:6559:1)
  at _update(./node_modules/vue/dist/vue.runtime.esm.js:3939:1)
  at call(./node_modules/vue/dist/vue.runtime.esm.js:4060:1)
  at get(./node_modules/vue/dist/vue.runtime.esm.js:4473:1)
  at new xn(./node_modules/vue/dist/vue.runtime.esm.js:4462:1)
  at vm(./node_modules/vue/dist/vue.runtime.esm.js:4067:1)
  at $mount(./node_modules/vue/dist/vue.runtime.esm.js:8409:1)
  at i(./node_modules/vue/dist/vue.runtime.esm.js:3118:1)
  at hydrate(./node_modules/vue/dist/vue.runtime.esm.js:6372:55)
  at hydrate(./node_modules/vue/dist/vue.runtime.esm.js:6405:1)
  at __patch__(./node_modules/vue/dist/vue.runtime.esm.js:6487:1)
  at _update(./node_modules/vue/dist/vue.runtime.esm.js:3939:1)
  at call(./node_modules/vue/dist/vue.runtime.esm.js:4060:1)
  at get(./node_modules/vue/dist/vue.runtime.esm.js:4473:1)
  at new xn(./node_modules/vue/dist/vue.runtime.esm.js:4462:1)
  at vm(./node_modules/vue/dist/vue.runtime.esm.js:4067:1)
  at $mount(./node_modules/vue/dist/vue.runtime.esm.js:8409:1)
  at i(./node_modules/vue/dist/vue.runtime.esm.js:3118:1)
  at hydrate(./node_modules/vue/dist/vue.runtime.esm.js:6372:55)
  at hydrate(./node_modules/vue/dist/vue.runtime.esm.js:6405:1)
  at __patch__(./node_modules/vue/dist/vue.runtime.esm.js:6487:1)
  at _update(./node_modules/vue/dist/vue.runtime.esm.js:3939:1)
  at call(./node_modules/vue/dist/vue.runtime.esm.js:4060:1)
  at get(./node_modules/vue/dist/vue.runtime.esm.js:4473:1)
  at new xn(./node_modules/vue/dist/vue.runtime.esm.js:4462:1)
  at vm(./node_modules/vue/dist/vue.runtime.esm.js:4067:1)
  at $mount(./node_modules/vue/dist/vue.runtime.esm.js:8409:1)
  at mount(./.nuxt/client.js:495:10)
  at call(./.nuxt/client.js:525:5)
  at tryCatch(./node_modules/regenerator-runtime/runtime.js:45:15)
  at _invoke(./node_modules/regenerator-runtime/runtime.js:271:1)
  at key(./node_modules/regenerator-runtime/runtime.js:97:1)
  at asyncGeneratorStep(./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3:1)
  at _next(./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:25:1)
TypeError: Cannot read property '0' of undefined
  at call(./node_modules/vue-lazy-hydration/dist/LazyHydrate.esm.js:397:6)
  at _render(./node_modules/vue/dist/vue.runtime.esm.js:3542:1)
  at call(./node_modules/vue/dist/vue.runtime.esm.js:4060:1)
  at get(./node_modules/vue/dist/vue.runtime.esm.js:4473:1)
  at run(./node_modules/vue/dist/vue.runtime.esm.js:4548:1)
  at flushSchedulerQueue(./node_modules/vue/dist/vue.runtime.esm.js:4304:1)
  at i(./node_modules/vue/dist/vue.runtime.esm.js:1980:1)
  at flushCallbacks(./node_modules/vue/dist/vue.runtime.esm.js:1906:1)

Support for hover for interactions

Thanks for the amazing plugin!
How can I have hover as a condition for lazyhydrate?
For example:

<LazyHydrate on-interaction="hover">

Prevents my Image Slider from being initialized

If I wrap around my Siema slider, it is never initialized. The "mounted"-hook (where I init the slider) is fired on page load, perhaps because the slider is close to the top of the page, but it is not initialized.

If I run the method manually, to init the slider, then it works fine.

Classes/Attrs not proxied upon client side mount

When an element is mounted client side, if it is not yet hydrated, this only renders a div, but when the Lazyhydrate element has a class applied to it, it will blow away any internal classes from the root slot element.

You can work around this at the moment by just never including classes or attributes on the LazyHydrate component, but that's a bit fragile since developers are quite used to classes/attrs being proxied through to the root child element I think (at least in our case this bit us fairly easily).

I created a small reproduction in https://github.com/brophdawg11/vue-ssr-lazy-hydration-classname-bug to show off the issue, and I have a proposed fix I will open in a PR shortly.

Thanks for the great work on this component! It has helped our performance metrics a ton in our project 👍

Add support of passive listener for scroll for lazy hydration on touchstart

Hi again,
Touch and wheel event listeners are useful for tracking user interactions and creating custom scrolling experiences, but they can also delay page scrolling. Currently, browsers can't know if an event listener will prevent scrolling, so they always wait for the listener to finish executing before scrolling the page. Passive event listeners solve this problem by letting you indicate that an event listener will never prevent scrolling.

If it's possible to add passive listener when there is support for it:
For something like this:

document.addEventListener('touchstart', onTouchStart, {passive: true});

Lazy hydrate long list [Question]

Hello! In the examples doesn't show how or if the library works with a list.
I have a really long grid with cards and would love if I not only lazy load the images, but also lazy-hydrate the cards.

It's something like this

<div class="grid">
  <transition-group name="zoom" tag="div" class="group">
    <Card :product="product" v-for="product in products" :key="product.id" />
  </transition-group>
</div>

Since I read that every component of the same type would load at the same time, I'm trying with

Card: hydrateWhenIdle(() => import('~/components/Card')),

But I'm not sure this library is suited for this problem. Any suggestions? Might be good to have a note about lists in the README :)

Thanks.

Temporary fix (plugin?) to block sending bundles?

Hello,

First of all, A great job on this component.

So, Because in vue template-renderer used by Nuxt the bundles are always sent,

Can we apply a (not necessarily pretty) workaround to remove them until this feature is supported (there is a pull request for this)?

Currently scoring green in Lighthouse is very important for us as for SEO improvement.

Thanks :)

The client-side rendered virtual DOM tree is not matching server-rendered content

Firstly thank you for creating this package—it looks awesome!...however I have not been able to get it to work 😕

I'm using Nuxt in universal mode, have copied your example, but am getting the following error in the console:

[Vue warn]: The client-side rendered virtual DOM tree is not matching
server-rendered content. This is likely caused by incorrect HTML markup, 
for example nesting block-level elements inside <p>, or missing <tbody>.
Bailing hydration and performing full client-side render.

I've tried configuring the LazyHydration component with ssr-only, when-visible and when-idle but get the same error.

I've created an example on Code Sandbox for you to review here: https://codesandbox.io/s/wwxw3x6qxk

I am using the LazyHydration component in layouts/default.vue to wrap the dynamically imported header component.

I thought that the issue might be because the header component is being imported using the src alias "~/", so I tried switching to a relative path—but still got the same error.

I then thought that it might be because the header component was using the <nuxt-link> component within it, so I replaced these with anchor tags—but still get the same error.

Any help would be very much appreciated!

Unable to run dev after installing package - Nuxt.js

First of all this seems like an amazing package!

I tried to test it on an existing project of mine which is built on Nuxt.js and facing some problems.

Steps

  1. yarn add vue-lazy-hydration
  2. Made the following changes:
//template
    <LazyHydrate when-visible>
      <div>
        <div class="container-fluid">
          <three-steps/>
        </div>
      </div>
    </LazyHydrate>

//script
import LazyHydrate from 'vue-lazy-hydration'
(...)
  components: {
    LazyHydrate,
    ThreeSteps: () => import('../components/Sections/Home/ThreeSteps')
  },
  1. yarn run dev

Outcome:

yarn run dev
yarn run v1.7.0
$ cross-env NODE_ENV=development nodemon server/index.js --watch server --watch serverMiddleware
[nodemon] 1.18.9
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: D:\Code\frontend\server/**/* D:\Code\frontend\serverMiddleware/**/*
[nodemon] starting `node server/index.js` 
[HPM] Proxy created: /  ->  https://xxxx.eu-central-1.es.amazonaws.com
[HPM] Proxy rewrite rule created: "^/api/es/" ~> "/"
[HPM] Subscribed to http-proxy events:  [ 'proxyReq', 'error', 'close' ]
i Preparing project for development
16:47:09    i Initial build may take a while
16:47:09    √ Builder initialized
16:47:11    Everything is ok
[nodemon] clean exit - waiting for changes before restart

It stops at "clean exit".

Advice Regarding Browser Support

Hey Markus,

Thanks for the hard work on this project – it's very much appreciated 👍

I've just received an event in Sentry alerting me to the fact that someone using Chrome ~v60 tripped over this package's usage of Map. This led me to wonder what your recommended usage of this component is regarding feature polyfills? I can see that things degrade gracefully if IntersectionObserver is unsupported, but there's no conditional logic around support for Map.

These are the 3 options I've come up with so far:-

  1. always import the Map polyfill from core-js
  2. include the LazyHydrate component in babel-loader rule (in which case Nuxt's config would handle polyfilling [unsure if double-transforming would cause any issues here])
  3. work out a way to import() the core-js polyfill, if support is missing, prior to the rest of the app running (probably via a Nuxt plugin in my case)

I look forward to hearing your thoughts and am happy to help with adding to the docs if it proves necessary.

How can I make it work with vue router?

Hi I have an app that im manually SSR it using vue-server-renderer, Aparrently seems that this plugin doesnt work for any of the components that I have inside VueRouter ... take this as an example:

<template>

    <div class="main">

        <LazyHydrate> <!-- works --->
            <MenuBar/>
        </LazyHydrate>

        <RouterView/>

        <LazyHydrate> <!-- works --->
            <Footer/>
        </LazyHydrate>

    </div>

</template>

<template>

    <Page class="page-a">

        <template #body>
            
            <LazyHydrate> <!-- doesnt work --->
                <SomeComponent/>
            </LazyHydrate>

        </template>

    </Page>

</template>

Do you know what might be happening here? should it work normally with RouterView ?

Try to understand the behavior

Hello,

I use nuxtjs v2.14.3 as universal and the latest beta version of vue-lazy-hydration.
I have a hidden navigation tool that I hydrate only when the tool is opened for the first time (ssr-only + trigger-hydration). It works as expected for no-mobile, but not for my mobile version (responsive).
Notice that nuxtjs generates the mobile version.

To be more precise, mobile and desktop are two different lazy components, called by dynamic component syntax:

<template>
  ...
    <LazyHydrate ssr-only :trigger-hydration="onceOpenNav">
      <component :is="isMobile ? 'HeaderMainNavMobilePanel' : 'HeaderMainNavPanel'>
        ...
      </component>
    </LazyHydrate>
  ...
</template>

export default {
  components: {
    HeaderMainNavPanel: () =>
      import('~/components/layout/header/HeaderMainNavPanel/HeaderMainNavPanel'),
    HeaderMainNavMobilePanel: () =>
      import('~/components/layout/header/HeaderMainNavMobilePanel/HeaderMainNavMobilePanel'),
  },
  ...
}

My problem is resolved by commenting this line: https://github.com/maoberlehner/vue-lazy-hydration/blob/master/src/LazyHydrate.js#L160

I'm surprising because:

  • when I put a console.log to inspect the this.$elt here, it is effectively an empty div - but only a few times, if I log this and I inspect it, there are some childrens ($elt in this as reference has been updated)
  • DOM that has been rendered by nuxtjs is mobile DOM so when I deactivate the javascript on browser side, I can see the childrens in the "empty div"
  • works as expected with no-mobile component (hydrate when trigger-hydration condition is true)

Does someone have an explanation, a trick, any idea ?

Regards

Error: "regeneratorRuntime is not defined" with Import Wrappers

First of all thank you for this project! Feels like must have for every vue based SSG. 👍

I tried vue-lazy-hydration with Gridsome. There is pretty large block of "static" markup without reactive data. Looks like perfect case for ssr-only property. It works perfectly with <LazyHydrate ssr-only> component, but when I tried Import Wrappers I got an error:

ReferenceError: regeneratorRuntime is not defined
    at resolvableComponentFactory (LazyHydrate.esm.js?8416:109)
    at hydrateSsrOnly

Chrome and Firefox, Vue 2.6.10

Here is my code:

<template>
  <Layout>
    <main class="block">
      <front-hero />
      <front-news />
      <catalog-block />
      <front-advantages />
    </main>

    <section class="services-wrapper">
      <div class="block">
        <front-services />
      </div>
    </section>
  </Layout>
</template>

<script>
import { hydrateSsrOnly } from 'vue-lazy-hydration'

export default {
  components: {
    FrontHero: hydrateSsrOnly(
      () => import('~/components/blocks/FrontHero.vue')
    ),
    FrontAdvantages: hydrateSsrOnly(
      () => import('~/components/blocks/FrontAdvantages.vue')
    ),
    FrontServices: hydrateSsrOnly(
      () => import('~/components/blocks/FrontServices.vue')
    ),
    FrontNews: hydrateSsrOnly(
      () => import('~/components/blocks/FrontNews.vue')
    ),
    CatalogBlock: hydrateSsrOnly(
      () => import('~/components/blocks/CatalogBlock.vue')
    )
  }
}
</script>

Using `ssr-only` preven't the element from appearing

If I use <LazyHydrate srr-only> around some static component, it is never shown. I tried changing it to when-visible and that works fine. Also I am not quite sure of the difference between the two, despiste having read the documentation and tutorial. Isn't when-visible better?

What is the role of ssr-only in hydration?

I have a site that is all optimized for SEO, but I am trying to improve its performance without falling into Google rank. So I saw this ssr-only parameter, I would like to know what it actually does and if it will help me.

Thank you.

BUG: TypeError: Cannot set property 'asyncFactory' of undefined

The component is loaded even when it was not visible

Code live: https://codesandbox.io/s/6yq314kkp3

image

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>

    <p v-for="x in 100" :key="x">
      scroll..
    </p>

    <LazyHydrate v-slot="{ hydrated }" when-visible>
      <foo v-if="hydrated"/>
    </LazyHydrate>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import LazyHydrate from 'vue-lazy-hydration'

export default {
  name: 'app',
  components: {
    HelloWorld,
    LazyHydrate,
    Foo: () => import(/* webpackChunkName: "component-foo" */ './components/Foo.vue'),
  }
}
</script>

Support for i18n sites

First of all; in general this plugin is top-notch. I really appreciate it, and I see it as an important plugin for any vue/nuxt project. It increased my pagespeed score 25 points on mobile with just a few lines of code. Thanks!

However, I noticed it doesn't work with nuxt-i18n, when switching locale. A full page refresh will do the trick though. I noticed perhaps this is because Nuxt is using vue version 2.6.11 and as you state in the documentation there is a bug in Vue that requires at least version 2.6.8? Perhaps that's the case (I hope), but then how did you manage to manually switch to this Vue version with your nuxt project?

Nested LazyHydrate

Currently it seems not possible to next .

I have a scenario where it makes sense to LazyHydrate a component, but if the parent component is also lazy hydrated, it won't work properly. Either it should be possible to nest LazyHydrate or I could apply a setting like: <LazyHydrate only-if="shouldHydrate"> where shouldHydrate is a boolean, I could define on the parent component, to whether or not the child components should be hydrated.

Say I have a ProductDisplay, ProductSlider and ProductList. The product list will have a lot of ProductDisplay, here I call on the ProductDisplay. It works. However, when I use my ProductSlider, which is also lazy hydrated, its child components (ProductDisplay) will not get rendered properly.

Prerender issues

I'm attempting to use this with prerender and I'm having siimilar issues to #15. Everything mostly works, but it's causing a full rerender of the page, rather than just hydrating of the given components. Am I doing anything immediately wrong here? Obviously it's tough to replicate this specific use case since it got an accompanying node server and middleware, but if you have any ideas let me know!

I'm mounting my app like this to ensure the JS is loaded correctly:

const root = new Vue({
  i18n,
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

document.addEventListener("DOMContentLoaded", function(event) {
  root.$mount('#app')
})

Then in App.vue I have:

<template>
  <div id="app" data-server-rendered="true">
      <div class="app-content">
        <router-view/>
      </div>
  </div>
</template>

Then lastly in my component I have:

<template>
  <div v-show="condition">
    <LazyHydrate ssr-only :trigger-hydration="condition">
      <div>
        <product-page v-if="condition"/>
      </div>
    </LazyHydrate>
  </div>
</template>

<script>
import LazyHydrate from 'vue-lazy-hydration';

export default {
  components: {
    ProductPage: () => import('views/product-page/product-page.vue'),
    LazyHydrate
  },
  ....
}

Am I doing something wrong here? This seems like it should work, but I'm getting mismatching node errors.

Parent:  <div data-v-36543689 class=​"app-content">​…​</div>​     vue.esm.js?a026:6040
Mismatching childNodes vs. VNodes:  NodeList [div] [VNode] .     vue.esm.js?a026:6041 
[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render. vue.esm.js?a026:591

Does this work for Nuxt’s Static Generation mode?

I’m very sorry for the stupid question – I can’t tell if this would work as described when an app is static generated using Nuxt.

I have an excessive number of DOM elements on my page and I want to reduce the amount of JS required on initial page load. As far as I can tell, even in Static Generation mode, Nuxt still has to render pages in a manner which would make vue-lazy-hydration beneficial. Is this incorrect?

Guidance much appreciated

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.