Vue 3-延迟加载图像组件

时间:2020-11-01 09:32:29

标签: javascript vue.js lazy-loading vuejs3 intersection-observer

使用Vue3,我正在尝试创建一个集成了lazyload的Picture组件。

当我加载页面并且所有图片都不在视口中时,一切正常(当我向下滚动时,图像会被连续加载)。

但是,当页面加载并且其中一个图片已经在视口中时,它将加载所有其他不可见的图像。

这里是图片组件

<template>
  <div
    class="picture"
    v-lazy-load="!!placeholder"
  >
    <img
      data-placeholder
      class="picture__placeholder"
      :src="/^http/.test(placeholder) ? placeholder : require(`@/assets/images/${placeholder}`)"
      v-if="placeholder"
    >
    <img
      data-image
      class="picture__image"
      :alt="alt"
      :data-url="/^http/.test(src) ? src : require(`@/assets/images/${src}`)"
    >
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { DirectiveElement } from '@/scripts/contracts/interfaces'

export default defineComponent({
  name: 'Picture',
  directives: {
    'lazy-load': {
      mounted(el: DirectiveElement, binding) {

        const image = el.querySelector('.picture__image') as HTMLImageElement

        if (!image) {
          console.error('[v-lazy-load] provided component doesn\'t contain element with class \'image\'')
          return false
        }

        if (typeof binding.value !== 'boolean') {
          console.error('[v-lazy-load] provided value is not a boolean')
          return false
        }

        if (Object.keys(binding.modifiers).length) console.warn('[v-lazy-load] no modifiers allowed')

        function loadImage() {
          if (image) {
            el.eventFn = () => el.classList.add('picture--loaded')
            image.addEventListener('load', el.eventFn)

            el.eventError = () => console.error('[v-lazy-load] error eventlistener')
            image.addEventListener('error', el.eventError)

            image.src = image.dataset.url as string
          }
        }

        function handleIntersect(
          entries: IntersectionObserverEntry[],
          observer: IntersectionObserver,
        ) {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              loadImage()
              observer.unobserve(el)
            }
          })
        }

        function createObserver() {
          const options = { root: null, threshold: 0 }
          const observer = new IntersectionObserver(handleIntersect, options)
          observer.observe(el)
        }

        if (window.IntersectionObserver && binding.value) return createObserver()
        return loadImage()
      },
      unmounted(el: DirectiveElement) {
        const image = el.querySelector('.picture__image') as HTMLImageElement

        if (image) {
          image.removeEventListener('load', el.eventFn as EventListener)
          image.removeEventListener('error', el.eventError as EventListener)
        }
      },
    },
  },
  props: {
    alt: { type: String, default: '' },
    src: { type: String, required: true },
    placeholder: { type: String, default: '' },
  },
})
</script>

此处是父级组件

<template>
  <div class="parent">

    <Picture
      src="desert.jpg"
      style="width: 80%;"
      placeholder="placeholder.png"
    />
    
    ...
    
    <Picture
      src="mountain.jpg"
      style="width: 80%;"
      placeholder="placeholder.png"
    />
  </div>
</template>

有人可以帮我吗?

0 个答案:

没有答案