IntersectionObserver/createIntersectionObserver实现滚动加载

443 阅读1分钟

在开发过程中如果有遇到滚动加载的需求,使用[IntersectionObserver] API (developer.mozilla.org/zh-CN/docs/…) 是一个非常不错的方案。IntersectionObserver有两个重要的点,一个的根元素(默认为视口),一个为交叉元素,废话不多说,我们直接上代码。

<template>
    <div class="">
        <ul class="list">
            <li v-for="(item, index) in data" :key="index">
                {{ item }}
            </li>
            <li class="loading">loading...</li>
        </ul>
    </div>
</template>

<template>
    <div class="">
        <ul class="list">
            <li v-for="(item, index) in data" :key="index">
                {{ item }}
            </li>
            <li class="loading">loading...</li>
        </ul>
    </div>
</template>

<script lang="ts" setup>
const data = ref([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
let ob = null
onMounted(() => {
    const list = document.querySelector(".list")
    const loading = document.querySelector(".loading")
    ob = new IntersectionObserver(function (entries) {
        const entry = entries[0]
        if (!entry.isIntersecting) {
            // 如果没有交叉 直接return
            return
        }
        // 该例交叉后去调loadMore方法(模拟接口返回数据) 实际根据自己场景去写业务
        loadMore()
    }, {
        root: list, // 和谁交叉 默认为视口   该demo为 loading类 和 list类交叉
        threshold: 1   // 阈值:0~1   即loading出现在list的比列,设置为1即完全暴露
    })
    ob.observe(loading as HTMLElement)  // 观察交叉的元素
})

async function loadMore() {
    setTimeout(() => {
        for (let index = 0; index < 10; index++) {
            data.value.push(index)
        }
    }, 300)
}

onUnmounted(() => {
    // 卸载实例,避免不必要的资源浪费
    ob!.unobserve(document.querySelector(".loading"))
})

</script>

<style scoped>
.list {
    height: 150px;
    overflow: auto;
    background-color: antiquewhite;
}
</style>

以上是PC端的一个简单例子,当然它还有更多的属性可供使用,想了解可查看MDN

接下来介绍一下小程序端关于该API的使用,由于uniapp和微信小程序并不支持该API的使用,不过官方也是有提供类似的API来供使用,下面我们以uniapp来做例子:

<template>
  <view>
    <scroll-view scroll-y="true" class="list">
      <view class="list-item" v-for="(item,i) in list" :key="i">
        {{item}}
      </view>
      <view class="load">
        我是一个Loading
      </view>
    </scroll-view>
  </view>

</template>

<script>
  export default {
    data() {
      return {
        _observer: null,
        list: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      }
    },
    onReady() {

    },
    onLoad() {
      // 第一个参数传入组件this
      this._observer = uni.createIntersectionObserver(this, {
        thresholds: 1, // 阈值:0~1
      })
      this._observer
        .relativeTo('.list', {
          bottom: 10 // bottom:10表示 load类进入 list类区域以下的10px就会触发  设置为0即交叉触发 (top/left/right同理)
        })
        .observe('.load', async (res) => {
          // 交叉后的回调
          // intersectionRatio 相交比列,由于滚动的时候加载完成数据intersectionRatio会变为0,为了防止再次调用loadMore,可以利用intersectionRatio的值作为条件
          if (res.intersectionRatio > 0) {
            await this.loadMore()
          }

        })
    },
    onUnload() {
      if (this._observer) this._observer = null
    },
    methods: {
      loadMore() {
        setTimeout(() => {
          for (let index = 0; index < 10; index++) {
            this.list.push(index)
          }
        }, 300)
      }
    }
  }
</script>

<style>
  .list {
    padding-left: 40rpx;
    box-sizing: border-box;
    height: 240px;
    border: 1px solid;
  }

  .list-item {
    height: 40px;
    line-height: 40px;
  }
</style>

以上就是uniapp关于该API的使用,微信小程序为wx.createIntersectionObserver(),用法同理。 谢谢您的观看,如有帮助,关注+点赞+收藏。如有问题,欢迎指正。