vue 结合 IntersectionObserver交叉观察器实现可视区域的加载

项目中遇到有一个图片列表,需要实时更新获取最新图片,随着滚动加载数据越来越多时,需要不停的发送大量的接口去查询图片信息。

此时可以使用IntersectionObserver来观察元素与视窗的交叉情况,在元素进入可视范围内,再去加载先关逻辑。这样可以优化页面加载首屏渲染的速度和滚动的性能

通过分装vue指令实现可视区域的加载

// observeVisibility.js

/**
 * @param {*} el 指定dom
 * @param {*} binding 绑定对象
 * parent:父级选择器
 * callback:在可视区域内的回调函数
 */
let observeVisibility = {}
observeVisibility.install = function (Vue) {
  Vue.directive('observe-visibility', {
    inserted (el, binding) {
      const options = binding.value || {}
      const parentSelector = options.parent || '' // 父级选择器
      const callback = options.callback || (() => {}) // 在可视区域内的回调函数

      const parentEl = parentSelector
        ? el.closest(parentSelector)
        : el.parentElement
      if (!parentEl) {
        console.warn(`Parent element not found for selector: ${parentSelector}`)
        return
      }

      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) { // el与parentEl是否交叉
            callback(true) //可视区域内
          } else {
            callback(false) //被遮住隐藏了
          }
        })
      }, { root: parentEl, threshold: 0.1 })

      observer.observe(el)
      el._observer = observer
      el._parentEl = parentEl
    },
    unbind (el, binding) {
      const options = binding.value || {}
      const callback = options.callback || (() => {}) // 在可视区域内的回调函数
      callback(false)
      if (el._observer) {
        el._observer.disconnect()
        delete el._observer
      }
      if (el._parentEl) {
        delete el._parentEl
      }
    }
  })
}
export default observeVisibility

main.js中引用: 

//main.js

import observeVisibility from '@/directive/observeVisibility'
Vue.use(observeVisibility)

 组件中使用:

<template>
  <div id="app">
    <div class="parent-container">
      <div class="item left"
           v-observe-visibility="{
             parent: '.parent-container',
             callback: (type) => fetchImageUrl(type,item.id)
           }"
           v-for="item in list"
           :key="item.id">
        <div class="item-content">
          <div class="title">{{item.imgName}}</div>
          <img :src="imageUrl[item.id]"
               alt="">
        </div>
      </div>
  </div>
</template>
export default {
  name: 'App',
  data () {
    return {
      list: [],
      imgIds: [],
    }
  },
  async created () {
    await this.getListFn()
    setInterval(() => {
      if (!this.imgIds.length) return
      this.getImgUrl(this.imgIds) // 可是区域的ids集合
    }, 1000)
  },
  methods: {
    handleImageIds (type, id) {
      const idx = this.imgIds.indexOf(id)
      if (type === true) { // 进入可视区域
        idx == -1 && this.imgIds.push(id)
      } else { // 超出可视区域
        idx != -1 && this.imgIds.splice(idx, 1)
      }
    },
    async getListFn () {
      // fetch data from server
      // ....
      this.list = []
    },
    getImgUrl (ids) {
      // fetch image url from server
      // ....

    }
  }
}
<style lang="less">
.parent-container {
  height: 80vh;
  overflow-y: auto;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值