Vue3封装hooks实现页面滚动触底刷新

1,876 阅读2分钟

实现页面滑动触底需要获取视口大小已滚动高度可滚动高度,可在页面注册scroll事件获取。 从下图可看出触底需要 已滚动高度 + 可视窗口高度 >= 可滚动区域 即可算出是否触底

1685372049039.png

组件中直接实现触底刷新

import {onMounted, onUnmounted } from 'vue'
const scroll = () => {
  const scrollHeight = document.documentElement.scrollHeight // 可滚动区域的高
  const scrollTop = document.documentElement.scrollTop // 已经滚动区域的高
  const clientHeight = document.documentElement.clientHeight // 可视区高度
  // 以滚动高度 + 当前视口高度  >= 可滚动高度 = 触底
  if (clientHeight + scrollTop >= scrollHeight) {
    // 此处可书写触底刷新代码
    console.log('触底')
  }
}
// 挂载dom后注册scroll事件
onMounted(() => window.addEventListener('scroll', scroll))
// 页面销毁移除scroll事件
onUnmounted(() => window.removeEventListener('scroll', scroll))

!!!注意

以上代码是基于window窗口滚动获取的值,如果组件中元素设置高度,并设置overflow-y: auto; 则滚动的是对应元素,此时需要获取滚动元素并注册scroll事件

<template>
  <div class="collect" ref="coRef">
    <template v-for="item in 100" :key="item">
      <div class="item"></div>
    </template>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const coRef = ref(null)
const scroll = () => {
  const scrollHeight = coRef.value.scrollHeight // 可滚动区域的高
  const scrollTop = coRef.value.scrollTop // 已经滚动区域的高
  const clientHeight = coRef.value.clientHeight // 可视区高度
  // 以滚动高度 + 当前视口高度  >= 可滚动高度 = 触底  
  if (clientHeight + scrollTop + 1 >= scrollHeight) { // 滚动区域很高时会有误差只需加上1px即可
    // 此处可书写触底刷新代码
    console.log('触底')
  }
}

// 挂载dom后注册scroll事件
onMounted(() => {
  coRef.value.addEventListener('scroll', scroll)
})
// 页面销毁移除scroll事件 -> 此处不用移除scroll事件,此页面销毁对应的coRef元素也随之销毁
//onUnmounted(() => coRef.value.addEventListener('scroll', scroll))
</script>

<style lang="less" scoped>
.collect {
  height: 100vh;
  overflow-y: auto;
  background-color: #42b780;
  .item {
    height: 100px;
    border: 1px solid #ccc;
  }
}
</style>

封装hooks

将以上两种情况结合封装更加通用的hooks

import { ref, onMounted, onUnmounted } from 'vue'
import { throttle } from 'lodash'

export default function useSroll(elRef) {
  // 默认监听window上的scroll事件
  let el = window
  const isBottom = ref(false) // 是否触底
  const scrollHeight = ref(0) // 可滚动区域的高
  const scrollTop = ref(0) // 已经滚动区域的高
  const clientHeight = ref(0) // 可视区高度

  // 使用lodash实现节流效果
  const scroll = throttle(() => {
    if (!elRef) {
      scrollHeight.value = document.documentElement.scrollHeight
      scrollTop.value = document.documentElement.scrollTop
      clientHeight.value = document.documentElement.clientHeight
    } else {
      scrollHeight.value = el.scrollHeight
      scrollTop.value = el.scrollTop
      clientHeight.value = el.clientHeight
    }
    // 以滚动高度 + 当前视口高度  >= 可滚动高度 = 触底
    if (scrollTop.value + clientHeight.value >= scrollHeight.value) {
      isBottom.value = true
    }
  }, 100)

  // 页面初始化注册scroll事件
  onMounted(() => {
    // dom 挂载时判断外界是否传入dom实例
    if (elRef) el = elRef.value
    el.addEventListener('scroll', scroll)
  })

  // 页面销毁时移除scroll事件
  onUnmounted(() => {
    el.removeEventListener('scroll', scroll)
  })

  return {
    isBottom,
    scrollHeight,
    scrollTop,
    clientHeight,
  }
}

组件中使用

组件中使用watch监听isBottom变化即可

import { watch } from 'vue'
import useSroll from '@/hooks/useSroll'


const { isBottom, scrollHeight, scrollTop, clientHeight } = useSroll() // 此处可传入滚动的dom实例

// 监听isBottom值变化
watch(isBottom, (newIsBottom) => {
  // newIsBottom如果true
  if (newIsBottom) {
    // 书写触底刷新代码, 记得刷新重置isBottom为false
  }
})