最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景

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

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

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

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

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

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

  • InfinitView 組件:將 InfinitView 組件包裹在列表(項(xiàng))外面即可實(shí)現(xiàn)無(wú)限加載。
  • 節(jié)流加載:每次觸底加載時(shí),會(huì)自動(dòng)節(jié)流,同一頁(yè)數(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)求哪一頁(yè)的數(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 無(wú)法監(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)非常簡(jiǎ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)求同一頁(yè)列表數(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)放在無(wú)限加載組件中進(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)放在無(wú)限加載組件中進(jìn)行管理。

因?yàn)樾枰獙?loading 狀態(tài)放在無(wú)限加載組件(子組件)中進(jìn)行管理,所以無(wú)限加載組件(子組件)必須要知道請(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簡(jiǎn)易版無(wú)限加載組件實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue無(wú)限加載組件實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Vue如何使用xlsx庫(kù)導(dǎo)出Excel文件

    詳解Vue如何使用xlsx庫(kù)導(dǎo)出Excel文件

    第三方庫(kù)xlsx提供了強(qiáng)大的功能來處理Excel文件,它可以簡(jiǎn)化導(dǎo)出Excel文件這個(gè)過程,本文將為大家詳細(xì)介紹一下它的具體使用,需要的小伙伴可以了解下
    2025-01-01
  • 解決Vue使用swiper動(dòng)態(tài)加載數(shù)據(jù),動(dòng)態(tài)輪播數(shù)據(jù)顯示白屏的問題

    解決Vue使用swiper動(dòng)態(tài)加載數(shù)據(jù),動(dòng)態(tài)輪播數(shù)據(jù)顯示白屏的問題

    今天小編就為大家分享一篇解決Vue使用swiper動(dòng)態(tài)加載數(shù)據(jù),動(dòng)態(tài)輪播數(shù)據(jù)顯示白屏的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • Vue3實(shí)現(xiàn)pdf預(yù)覽功能

    Vue3實(shí)現(xiàn)pdf預(yù)覽功能

    在開發(fā)中,PDF預(yù)覽和交互功能是一個(gè)常見的需求,無(wú)論是管理系統(tǒng)、在線教育平臺(tái),還是企業(yè)內(nèi)部的知識(shí)庫(kù),能夠高效地展示和操作PDF文件都能極大地提升用戶體驗(yàn),本文將詳細(xì)介紹如何在Vue項(xiàng)目中實(shí)現(xiàn)pdf預(yù)覽功能,需要的朋友可以參考下
    2025-03-03
  • vue 實(shí)現(xiàn)在函數(shù)中觸發(fā)路由跳轉(zhuǎn)的示例

    vue 實(shí)現(xiàn)在函數(shù)中觸發(fā)路由跳轉(zhuǎn)的示例

    今天小編就為大家分享一篇vue 實(shí)現(xiàn)在函數(shù)中觸發(fā)路由跳轉(zhuǎn)的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • Vue使用Tinymce富文本自定義toolbar按鈕的實(shí)踐

    Vue使用Tinymce富文本自定義toolbar按鈕的實(shí)踐

    本文主要介紹了Vue使用Tinymce富文本自定義toolbar按鈕,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • VUE中常用的4種高級(jí)方法

    VUE中常用的4種高級(jí)方法

    provide/inject 是 Vue.js 中用于跨組件傳遞數(shù)據(jù)的一種高級(jí)技術(shù),它可以將數(shù)據(jù)注入到一個(gè)組件中,然后讓它的所有子孫組件都可以訪問到這個(gè)數(shù)據(jù),這篇文章主要介紹了VUE中常用的4種高級(jí)方法,需要的朋友可以參考下
    2023-05-05
  • 如何在vue-cli中使用css-loader實(shí)現(xiàn)css module

    如何在vue-cli中使用css-loader實(shí)現(xiàn)css module

    這篇文章主要介紹了如何在vue-cli中使用css-loader實(shí)現(xiàn)css module,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2021-01-01
  • 手把手教你搭建vue3.0項(xiàng)目架構(gòu)

    手把手教你搭建vue3.0項(xiàng)目架構(gòu)

    這篇文章手把手教你搭建vue3.0項(xiàng)目架構(gòu),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2021-11-11
  • vue-cli webpack 開發(fā)環(huán)境跨域詳解

    vue-cli webpack 開發(fā)環(huán)境跨域詳解

    本篇文章主要介紹了vue-cli webpack 開發(fā)環(huán)境跨域詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • echarts實(shí)現(xiàn)獲取datazoom的起始值(包括x軸和y軸)

    echarts實(shí)現(xiàn)獲取datazoom的起始值(包括x軸和y軸)

    這篇文章主要介紹了echarts實(shí)現(xiàn)獲取datazoom的起始值(包括x軸和y軸),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07

最新評(píng)論

鸡西市| 宁海县| 稷山县| 乐至县| 固始县| 东至县| 亚东县| 财经| 盘锦市| 滁州市| 益阳市| 元江| 仙游县| 沙湾县| 南平市| 望城县| 苏尼特左旗| 闻喜县| 十堰市| 新郑市| 莱西市| 随州市| 微博| 山东| 永济市| 高尔夫| 琼海市| 丹江口市| 合江县| 盐边县| 郧西县| 铁岭县| 枣强县| 陵水| 广德县| 沭阳县| 石渠县| 兰西县| 和平县| 新津县| 万年县|