欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue簡易版無限加載組件實(shí)現(xiàn)原理與示例代碼

 更新時(shí)間:2022年07月04日 08:46:14   作者:kzkz  
這篇文章主要給大家介紹了關(guān)于Vue簡易版無限加載組件實(shí)現(xiàn)原理與示例代碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

背景

遇到的兩個(gè)問題:scroll 事件不觸發(fā)、如何將 loading 狀態(tài)放在無限加載組件中進(jìn)行管理。

無限加載組件在展示列表頁數(shù)據(jù)時(shí)比較常見。特別是在 H5 列表頁中,數(shù)據(jù)比較多,需要做分頁,無限加載組件就是一個(gè)非常好的選擇。

當(dāng)列表頁數(shù)據(jù)比較多時(shí),一次性從服務(wù)端拿到所有的數(shù)據(jù)會(huì)比較耗時(shí),長時(shí)間不展示列表數(shù)據(jù),比較影響用戶體驗(yàn)。所以對(duì)于一般的長列表數(shù)據(jù),都會(huì)做分頁。

首次請(qǐng)求時(shí),只請(qǐng)求第一頁數(shù)據(jù);當(dāng)用戶上拉即將到達(dá)列表底部時(shí),再請(qǐng)求下一頁數(shù)據(jù),將下一頁數(shù)據(jù)拼接在之前的列表后。

實(shí)現(xiàn)功能

使用 vue3 composition API 實(shí)現(xiàn)如下功能:

  • InfinitView 組件:將 InfinitView 組件包裹在列表(項(xiàng))外面即可實(shí)現(xiàn)無限加載。
  • 節(jié)流加載:每次觸底加載時(shí),會(huì)自動(dòng)節(jié)流,同一頁數(shù)據(jù)只會(huì)請(qǐng)求一次(如果請(qǐng)求成功)。

注意:InfinitView 直接子元素高度需要比 InfinitView 組件高,才會(huì)觸發(fā)滾動(dòng)加載。InfinitView 組件的高度默認(rèn)為其父元素的 100%。

Props

// 觸底距離,當(dāng)距底部距離小于等于 distance 時(shí),會(huì)觸發(fā)加載函數(shù)
distance: {
  type: Number,
  default: 30,
},
// 加載函數(shù),觸底時(shí)執(zhí)行
onload: {
  type: Function,
  default: async () => {},
},
// 行內(nèi)樣式,在外部可以通過 classStyle 改變 InfinitView 組件的樣式
classStyle: {
  type: Object,
  default: () => ({}),
},

使用

直接將 InfiniteView 組件包裹在列表項(xiàng)外面即可:

<InfiniteView :onload="onload">
  <div v-for="item in list">
    {{ item }}
  </div>
</InfiniteView>

使用 setTimeout 模擬列表數(shù)據(jù)的加載:

// nextPage 表示下一次請(qǐng)求哪一頁的數(shù)據(jù)
const nextPage = ref(1);
// list 表示數(shù)據(jù)列表
const list = ref(new Array(30).fill(0));

const onload = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      list.value.push(...new Array(50).fill(0).map((_, i) => i + 1 + (nextPage.value - 1) * 50));
      nextPage.value++;
      resolve(nextPage.value);
    }, 200);
  });
}

使用時(shí)需要注意:InfinitView 組件的高度默認(rèn)為其父元素的 100%,如果其父元素高度不確定(例如:由子元素?fù)伍_),會(huì)導(dǎo)致 InfinitView 無法監(jiān)聽到滾動(dòng)事件,也就不會(huì)觸發(fā) onload 函數(shù)(后面會(huì)解釋原因)。

解決方案有兩種

  • 為 InfinitView 組件的父元素設(shè)置一個(gè)可計(jì)算的高度。
  • 為 InfinitView 組件設(shè)置一個(gè)可計(jì)算的高度,可通過其 props 行內(nèi)樣式 classStyle 設(shè)置,或者在外部給 InfinitView 組件加上類名及其樣式。

注意:這里的可計(jì)算高度可以是:由 flex 彈性容器計(jì)算得來,但不能由子元素(InfinitView)撐開得來。

組件實(shí)現(xiàn)

組件的實(shí)現(xiàn)非常簡單,InfinitView 組件實(shí)際上就是一個(gè) div,只不過在 InfinitView 內(nèi)部監(jiān)聽了該 div 的滾動(dòng)事件。在即將觸底的時(shí)候去調(diào)用從父組件中傳過來的 onload 函數(shù)。

其 template 實(shí)現(xiàn)如下:

<div
  class="infinite-view"
  :style="classStyle"
  @scroll="onScroll($event.target)"
>
  <slot />
</div>
  • 可以通過 classStyle 在外部設(shè)置 InfinitView 組件的樣式。
  • 觸發(fā)滾動(dòng)事件的時(shí)候,會(huì)執(zhí)行 onScroll 函數(shù)。onScroll 函數(shù)中屏蔽了調(diào)用 onload 函數(shù)的細(xì)節(jié)(觸底加載、節(jié)流加載)。
  • 使用 slot 將 InfinitView 的子組件當(dāng)成該 div 的子組件。

其 style 樣式如下:

.infinite-view {
  height: 100%;
  overflow-y: scroll;
}

InfinitView 組件的高度由其父元素決定,默認(rèn)為其父元素高度的 100%,這就限制了其父元素的高度不能由 InfinitView 撐開決定。

其 script 如下:

import { ref, defineProps } from 'vue';

const props = defineProps({
  distance: {
    type: Number,
    default: 30,
  },
  onload: {
    type: Function,
    default: async () => {},
  },
  classStyle: {
    type: Object,
    default: () => ({}),
  },
});

const isloading = ref(false);

const onScroll = async (element) => {
  if (isloading.value) {
    return;
  }
  if (element.scrollHeight <= element.scrollTop + element.offsetHeight + props.distance) {
    try {
      isloading.value = true;
      await props.onload();
      isloading.value = false;
    } catch (error) {
      console.log(error);
      isloading.value = false;
    }
  }
}
  • 判斷觸底條件:scrollHeight <= scrollTop + clientHeight + distance

  • scrollHeight 代表整個(gè)滾動(dòng)區(qū)域的高度。
  • scrollTop 是向上滾動(dòng)的距離,即從內(nèi)容區(qū)頂部到整個(gè)滾動(dòng)區(qū)域頂部的距離。
  • clientHeight 是內(nèi)容區(qū)的高度。
  • distance 代表觸底的距離,它是一個(gè)緩沖距離,即在內(nèi)容區(qū)底部距離整個(gè)滾動(dòng)區(qū)域底部距離小于等于 distance 時(shí),會(huì)觸發(fā) onload 函數(shù)。

當(dāng) scrollHeight === scrollTop + clientHeight 時(shí),剛好滑動(dòng)到底部。一般情況下,我們可以提前加載,設(shè)置一個(gè)緩沖距離 distance,當(dāng)即將滑動(dòng)到底部,距底部不足 distance 的距離時(shí),就可以觸發(fā)加載函數(shù)。

  • isloading 用來控制加載的狀態(tài),并實(shí)現(xiàn)節(jié)流加載。

加載函數(shù) onload 一般是異步函數(shù),用來請(qǐng)求列表數(shù)據(jù)。在執(zhí)行 onload 函數(shù)之前,將 isloading 設(shè)為 true,表示正在加載中;當(dāng) onload 函數(shù)執(zhí)行完之后,將 isloading 設(shè)為 false,表示加載狀態(tài)結(jié)束。

我們知道,scroll 事件是會(huì)頻繁觸發(fā)的,只要列表在滾動(dòng),onScroll 函數(shù)就會(huì)一直執(zhí)行。

這就有可能導(dǎo)致:當(dāng)滑動(dòng)至距離底部不足 distance 距離時(shí),滿足觸底條件,列表還在持續(xù)滾動(dòng),此時(shí)就會(huì)持續(xù)執(zhí)行 onload 函數(shù)發(fā)送請(qǐng)求,即使上一次請(qǐng)求還沒回來,瀏覽器也會(huì)持續(xù)請(qǐng)求同一頁列表數(shù)據(jù)。

所以需要實(shí)現(xiàn)節(jié)流加載,控制 onload 函數(shù)的執(zhí)行頻率。如果上一次請(qǐng)求還沒回來,則不執(zhí)行 onload 函數(shù)。也就是在觸底條件之前,如果上一次請(qǐng)求還在加載中,直接 return 掉。

const onScroll = async (element) => {
  // 如果上一次請(qǐng)求還沒回來,直接 return
  if (isloading.value) {
    return;
  }
  if (element.scrollHeight <= element.scrollTop + element.offsetHeight + props.distance) {
    try {
      // 在請(qǐng)求之前將 isloading 置為 true
      isloading.value = true;
      await props.onload();
      // 請(qǐng)求成功之后將 isloading 置為 false
      isloading.value = false;
    } catch (error) {
      console.log(error);
      // 請(qǐng)求失敗之后也將 isloading 置為 false
      isloading.value = false;
    }
  }
}

實(shí)現(xiàn)的時(shí)候有兩個(gè)細(xì)節(jié)需要提一下:scroll 事件不觸發(fā)、如何將 loading 狀態(tài)放在無限加載組件中進(jìn)行管理。

scroll 事件

有時(shí)候經(jīng)常會(huì)遇到屏幕在滾動(dòng),但是一直沒有觸發(fā) scroll 事件。那是因?yàn)殡m然屏幕滾動(dòng)了,但是監(jiān)聽 scroll 事件的 div 并沒有滾動(dòng)。

當(dāng)然我們可以省事地為 window 設(shè)置監(jiān)聽 scroll 的事件,不管是哪個(gè)元素觸發(fā)的 scroll 事件,最終都會(huì)冒泡到 window 上面,設(shè)置的 scroll 回調(diào)函數(shù)也總是會(huì)執(zhí)行。

// 在 InfinitView 中,組件掛載之后,為 window 設(shè)置監(jiān)聽 scroll 的事件
onMounted(() => {
  window.addEventListener('scroll', (e) => {
    onScroll(e.target);
  })
})

上面的代碼在 InfinitView 組件中,為 window 設(shè)置了監(jiān)聽 scroll 的事件。當(dāng)屏幕滾動(dòng)時(shí),就會(huì)執(zhí)行 onScroll 函數(shù)。這樣也是沒問題的,確實(shí)可以解決 scroll 事件不觸發(fā)的問題。

但是我們并沒有找到問題的根源,為什么在 InfinitView 組件中的 div 上面監(jiān)聽的 scroll 事件,卻不會(huì)觸發(fā)?

首先得知道什么情況下才會(huì)觸發(fā)滾動(dòng)事件

  • 父元素高度比其所有子元素高度之和小。
  • 父元素的 overflow 屬性值為:auto | scroll

只有滿足了以上兩個(gè)條件,才會(huì)觸發(fā)父元素的 scroll 事件。很多時(shí)候,某個(gè) div 的 scroll 事件沒有觸發(fā),是因?yàn)槲覀儧]有設(shè)置該 div 的高度,它的高度由子元素?fù)伍_,和子元素高度之和相等。

這樣即使屏幕在滾動(dòng),觸發(fā)的也不是它的 scroll 事件,而是更上層 div 的 scroll 事件。例如:如果某個(gè) div 的高度由子元素?fù)伍_,并且其父元素高度確定,比它的高度小,則在滾動(dòng)的時(shí)候,不會(huì)觸發(fā)該 div 的 scroll 事件,會(huì)觸發(fā)它的父元素的 scroll 事件。

或者我們忘記設(shè)置監(jiān)聽 scroll 事件的元素的 overflow 屬性了,默認(rèn)情況下,overflow 的值為 visible。

$emit 發(fā)射事件和 props 回調(diào)函數(shù)的區(qū)別

我們知道在 Vue 中,子組件向父組件通信有兩種方式:通過 $emit 發(fā)射事件、通過調(diào)用父組件中傳過來的回調(diào)函數(shù)。

這兩種方式都可以由子組件向父組件通信,但是也有一些細(xì)微的區(qū)別:

  • 通過調(diào)用父組件中傳過來的回調(diào)函數(shù)可以拿到函數(shù)的返回值,而通過 $emit 發(fā)射事件不可以。
  • 通過調(diào)用父組件中傳過來的回調(diào)函數(shù)可以知道函數(shù)什么時(shí)候執(zhí)行完,而通過 $emit 發(fā)射事件不可以,它只能將回調(diào)函數(shù)通過 $emit 的參數(shù),傳給父組件,強(qiáng)迫父組件顯式調(diào)用,才能在函數(shù)執(zhí)行完之后做一些事情。

看起來,好像使用 props 回調(diào)函數(shù)比 $emit 發(fā)射事件要更好,那是不是 $emit 發(fā)射事件就沒有好處了呢?

也不是。從名字就可以看出,$emit 發(fā)射事件,是從子組件中發(fā)射一個(gè)事件給父組件,父組件在監(jiān)聽到子組件發(fā)射的事件之后,可以進(jìn)行一系列的操作。它只是給父組件發(fā)射一個(gè)事件,傳遞一個(gè)信號(hào)給父組件,父組件接收到這個(gè)信號(hào)之后,接下來要怎么做還是由父組件決定。但是使用 props 回調(diào)函數(shù)的方式則不同,它是將父組件中的一個(gè)函數(shù)通過 props 傳給子組件,子組件拿到這個(gè)回調(diào)函數(shù)之后,要怎么執(zhí)行,完全取決于子組件。所以子組件可以知道回調(diào)函數(shù)什么時(shí)候執(zhí)行完,也可以拿到回調(diào)函數(shù)的返回值。

了解了這兩種通信方式的區(qū)別,就解決了如何將 loading 狀態(tài)放在無限加載組件中進(jìn)行管理。

因?yàn)樾枰獙?loading 狀態(tài)放在無限加載組件(子組件)中進(jìn)行管理,所以無限加載組件(子組件)必須要知道請(qǐng)求什么時(shí)候回來,也就是 onload 異步函數(shù)什么時(shí)候執(zhí)行完。

這樣我們就可以用 props 回調(diào)函數(shù)的形式,將父組件中的異步請(qǐng)求函數(shù)傳給子組件,當(dāng)列表即將滾動(dòng)到底部時(shí),將 loading 狀態(tài)置為 true,然后發(fā)送請(qǐng)求,當(dāng)請(qǐng)求回來之后,異步函數(shù)執(zhí)行完,再將 loading 狀態(tài)置為 false。如果請(qǐng)求沒有回來,loading 狀態(tài)為 true,即使再次觸發(fā)了 scroll 事件,也直接返回,不再繼續(xù)發(fā)送請(qǐng)求。

const onScroll = async (element) => {
  // 如果上一次請(qǐng)求還沒回來,直接 return
  if (isloading.value) {
    return;
  }
  if (element.scrollHeight <= element.scrollTop + element.offsetHeight + props.distance) {
    try {
      // 在請(qǐng)求之前將 isloading 置為 true
      isloading.value = true;
      await props.onload();
      // 請(qǐng)求成功之后將 isloading 置為 false
      isloading.value = false;
    } catch (error) {
      console.log(error);
      // 請(qǐng)求失敗之后也將 isloading 置為 false
      isloading.value = false;
    }
  }
}

總結(jié)

到此這篇關(guān)于Vue簡易版無限加載組件實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue無限加載組件實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于element中表格和表單的封裝方式

    關(guān)于element中表格和表單的封裝方式

    這篇文章主要介紹了關(guān)于element中表格和表單的封裝方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue版數(shù)字翻牌器的封裝

    vue版數(shù)字翻牌器的封裝

    這篇文章主要為大家詳細(xì)介紹了vue版數(shù)字翻牌器的封裝,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue-loader中引入模板預(yù)處理器的實(shí)現(xiàn)

    vue-loader中引入模板預(yù)處理器的實(shí)現(xiàn)

    這篇文章主要介紹了vue-loader中引入模板預(yù)處理器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Vue中 Runtime + Compiler 和 Runtime-only 兩種模式含義和區(qū)別詳解

    Vue中 Runtime + Compiler 和 Runtime-o

    這篇文章主要介紹了Vue中 Runtime + Compiler 和 Runtime-only 兩種模式含義和區(qū)別,結(jié)合實(shí)例形式詳細(xì)分析了Vue中 Runtime + Compiler 和 Runtime-only 兩種模式基本功能、原理、區(qū)別與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2023-06-06
  • 在線使用iconfont字體圖標(biāo)的簡單實(shí)現(xiàn)

    在線使用iconfont字體圖標(biāo)的簡單實(shí)現(xiàn)

    這篇文章主要介紹了在線使用iconfont字體圖標(biāo)的簡單實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue使用highcharts自定義圖例點(diǎn)擊事件

    vue使用highcharts自定義圖例點(diǎn)擊事件

    這篇文章主要為大家詳細(xì)介紹了vue使用highcharts自定義圖例點(diǎn)擊事件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue+element自定義查詢組件

    vue+element自定義查詢組件

    這篇文章主要為大家詳細(xì)介紹了vue+element自定義查詢組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 使用Vue3和Echarts?5繪制帶有立體感流線中國地圖(推薦收藏!)

    使用Vue3和Echarts?5繪制帶有立體感流線中國地圖(推薦收藏!)

    最近接到一個(gè)需求是做一個(gè)中國地圖,下面這篇文章主要給大家介紹了關(guān)于如何使用Vue3和Echarts?5繪制帶有立體感流線中國地圖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • 基于vue實(shí)現(xiàn)多功能樹形結(jié)構(gòu)組件的示例代碼

    基于vue實(shí)現(xiàn)多功能樹形結(jié)構(gòu)組件的示例代碼

    一個(gè)優(yōu)雅展示樹形結(jié)構(gòu)數(shù)據(jù)的 Vue 組件,遞歸渲染每個(gè)節(jié)點(diǎn)及其子節(jié)點(diǎn),支持自定義顏色、文本和布局,通過獨(dú)特的樣式巧妙處理不同層級(jí),為用戶打造豐富的視覺盛宴,文中通過代碼給大家介紹的非常詳細(xì),感興趣的同學(xué)可以自己動(dòng)手嘗試一下
    2024-02-02
  • Vue中watch與watchEffect的區(qū)別詳細(xì)解讀

    Vue中watch與watchEffect的區(qū)別詳細(xì)解讀

    這篇文章主要介紹了Vue中watch與watchEffect的區(qū)別詳細(xì)解讀,watch函數(shù)與watchEffect函數(shù)都是監(jiān)聽器,在寫法和用法上有一定區(qū)別,是同一功能的兩種不同形態(tài),底層都是一樣的,需要的朋友可以參考下
    2023-11-11

最新評(píng)論