前端 | 頁面觸底自動加載 Vue 組件


不管是 web 端還是移動端,信息流都是現在很流行的信息展示方式。信息流經常搭配自動加載一起使用以獲得更好的使用體驗。

最近在使用 Vue 開發過程中也遇到了首頁信息流自動加載的需求。大致了解了一下幾個滾動自動加載組件,發現多數都是把內容放在在一個單獨的滾動容器內;但我遇到的需求是整個頁面的滾動(博客列表首頁那種),不是限制在容器內,不太符合。把整個頁面放進滾動容器明顯很奇怪,只是為了一個簡單的下拉加載不值當。所以參考網上的一些介紹實現了一個幾十行的簡單小組件 ButtomDetector 來實現這個功能,同時也方便在多個需要到底自動加載的頁面中進行復用。

實現原理

實現原理非常簡單:

  • 監聽頁面的滾動事件
  • 在觸發滾動時,調用 DOM api 來獲取頁面的滾動狀態
  • 如果已經滾動到底,拋出事件供頁面使用

JS 事件監聽

// 監聽 scroll 事件,綁定回調函數
window.addEventListener('scroll', this.listenBottomOut)

// 取消事件監聽
window.removeEventListener('scroll', this.listenBottomOut, false)

頁面滾動狀態

DOM 提供了獲取頁面元素滾動狀態的相關屬性,這里主要用到以下三個:scrollHeight 滾動內容高度,scrollTop 滾動內容頂部離顯示區域的距離,clientHeight 顯示區域高度。如下圖所示。

image-20210829001533159

因此,判斷滾動到底就很簡單了:

if (scrollTop + clientHeight >= scrollHeight - delta) {
    // emit bottom event
}

其中 delta 的作用是可以在快到底端(例如還有50px)時提前進行加載,避免拉到底后等待加載的這段時間。

一些細節問題

重復拋出事件

因為監聽的是 scroll 事件,並且使用 delta 做了一點提前加載,因此可能會觸發多次觸底事件(距離底部48px, 30px, 12px... 都符合判斷條件),造成頁面重復多次加載數據。因此選擇在組件中增加一個 loadingMore 屬性,表示父頁面正在加載數據,此時不再繼續拋出事件;當頁面完成加載后,將 loadingMore 重置為 false 以繼續監聽觸底。

沒有更多數據

在信息流已經到底,沒有更多數據的時候,觸底事件就沒有作用了。當然這個判斷也可以在父頁面中進行,當沒有更多數據時不再繼續處理觸底事件;不過為了方便頁面中的加載數據方法(不用每次都單獨判斷是否有更多),給組件增加一個 noMore 參數,此時不再拋出觸底事件。

代碼實現

bottomDetector 組件

<template>
  <div style="text-align: center">
    <div v-if="loadingMore">加載中</div>
    <div v-if="noMore">沒有更多了</div>
  </div>
</template>
<script>
export default {
  props: {
    loadingMore: {
      type: Boolean,
      required: true,
    },
    distance: {
      type: Number,
      default: 50,
    },
    noMore: {
      type: Boolean,
      default: false,
    },
  },
  mounted() {
    window.addEventListener('scroll', this.listenBottomOut)
    this.element = document.documentElement
  },
  destroyed() {
    window.removeEventListener('scroll', this.listenBottomOut, false)
  },
  data() {
    return {
      element: null,
    }
  },
  methods: {
    listenBottomOut() {
      if (this.noMore || this.loadingMore) return
      let scrollTop = this.element.scrollTop || document.body.scrollTop
      let clientHeight = this.element.clientHeight
      let scrollHeight = this.element.scrollHeight
      if (scrollTop + clientHeight >= scrollHeight - this.distance) {
        this.$emit('arriveBottom')
      }
    },
  },
}
</script>

頁面使用

從觸底功能上來說,detector 組件放在頁面中的任何位置都可以。放在信息流下方的話還可以順便做加載動畫和信息到底的提示信息。

<div class="infos">
  <div v-for="item in infos" :key="item.id">
    <!-- info -->
  </div>
</div>
<bottom-detector
  :loadingMore="infosLoadingMore"
  :noMore="infos.length >= infoTotal"
  @arriveBottom="getMoreInfo"
/>
</div>
getMoreInfo() {
  this.infosLoadingMore = true
  // loading more info
  this.infosLoadingMore = false
},

結語

以上是簡單的 Vue 頁面觸底加載組件的原理及實現,希望對你有所幫助,歡迎在評論區進行討論或指正。

監聽事件函數部分參考了博客:觸底加載更多(原理 + 在vue中的使用)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM