NUXT | Как реализовать ленивую загрузку компонента nuxt? (ответ внутри)
Условия:
- Дан компонент, который я хочу загрузить лениво и который находится вне viewport.
- Чтобы использовать 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
})
}
Но компонент у меня используется на многих страницах, потому такой способ не очень подходит.
------------------------------------ РЕШЕНИЕ ------------------------------------
Размещаю решение, которое я использую сейчас:
- Создаем компонент: --> 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>
- Оборачиваем компонент, который хотим загружать лениво:
<visible v-slot:default="slot">
<LazyProducts :products="products" v-if="slot.visible" />
</visible>