NUXT | Как реализовать ленивую загрузку компонента nuxt? (ответ внутри)

Условия:

  1. Дан компонент, который я хочу загрузить лениво и который находится вне viewport.
  2. Чтобы использовать lazy- префикс я должна добавить условие v-if, по которому компонент будет загружаться лениво, но у меня просто компонент, который расположен вне зоны видимости пользователя при первой загрузке. Единственный способ добавить v-if это костыль в виде
mounted (){
   setTimeout(() => {
     this.showLazyComponent = true
  }, 3000)
}

Но это решение мне не нравится. Для чего мне нужно компонент сделать ленивым - хочу улучшить performance в Lighthouse.

P.s. Пока придумала другой костыль: Делать обертку

<div ref="lazy">
      <LazyNews
        :content="news"
        v-if="showLazy"
      />
    </div>

И использовать ее так (с помощью IntersectionObserver):

data() {
    return {
      showLazy: false
    }
},
mounted() {
    initObserver(this.$refs.lazy).then(() => {
      this.showLazy = true
    })
}

Но компонент у меня используется на многих страницах, потому такой способ не очень подходит.

------------------------------------ РЕШЕНИЕ ------------------------------------

Размещаю решение, которое я использую сейчас:

  1. Создаем компонент: --> components/Visible.vue
<script lang="ts">
import Vue, { CreateElement, VNode, RenderContext } from "vue"
import {DefaultProps} from "vue/types/options";

export default Vue.extend({
  name: "Visible",
  data() {
    return {
      visible: false,
      observer: null as IntersectionObserver|null,
      rootElement: null
    }
  },
  mounted() {
    this.updateRootElement()
    this.initObserver()
  },
  updated() {
    this.updateRootElement()
  },
  beforeDestroy() {
    this.destroyObserver()
  },
  methods: {
    destroyObserver() {
      if (this.observer) {
        this.observer.disconnect()
        this.observer = null
      }
    },
    initObserver() {
      this.destroyObserver()

      if (!this.$isServer) {
        this.observer = new IntersectionObserver(async (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {

          this.visible = entries.some(entry => entry.intersectionRatio > 0)
        })

        this.observe(this.rootElement)
      }
    },
    isElement(element) {
      return typeof Element !== 'undefined' && element instanceof Element
    },
    updateRootElement() {
      this.rootElement = this.$el
    },
    observe(element) {
      if (this.isElement(element)) this.observer.observe(element)
    },
    unobserve(element) {
      if (this.isElement(element)) this.observer.unobserve(element)
    },
  },
  render(createElement: CreateElement, context: RenderContext<DefaultProps>): VNode {
    if(this.visible) {
      this.unobserve(this.rootElement)
    }
    const slotContent = this.$scopedSlots.default && this.$scopedSlots.default({visible: this.visible})
    const slotRoot = Array.isArray(slotContent) ? slotContent.pop() : null

    return slotRoot || createElement('span', {class: 'visible-empty'})
  }
})
</script>

<style scoped>
.visible-empty {
  display: block;
  height: 0;
  width: 0;
}
</style>

  1. Оборачиваем компонент, который хотим загружать лениво:
<visible v-slot:default="slot">
      <LazyProducts :products="products" v-if="slot.visible" />
</visible>

Ответы (0 шт):