Vue 虛擬列表的實戰示例
序言
現如今,我們總是在無止境的刷。刷微博、刷抖音、刷沸點……一次次絲滑下拉體驗的背後卻是前端攻城獅的用心。
本篇討論基於 Vue.js 的列表無限下拉實踐。
我們的目標就是:讓列表下拉縱享絲滑,而不是像以往的下拉就 loading 等待的體驗。
- 譯自 Better Programming
- 在線 Demo
設計
咱還是用 Vue CLI 來快速構建項目。
這是主頁面:
// EndlessList.vue
<template> <div class="endless-scrolling-list"> <!-- 搜索框 --> <div class="search-box"> <input type="text" v-model="searchQuery"/> </div> <p class="center" v-if="results.length == 0 && !loading"> Start typing to search something. </p> <!-- 虛擬列表 --> <virtual-list :data-key="'pageid'" :data-sources="results" :data-component="itemComponent" :page-mode="true" /> <!-- loading --> <loader v-if="loading" /> </div> </template>
其中核心當然是virtual-list組件啦~
這裡的虛擬列表,我們用到一個三方庫 Vue Virtual Scroll List,它在 Github 上又 2.5k+ 的 stars。類比於 react 的 react-virtualized 庫。
大量的 DOM 元素會使得我們的網頁非常“重”。當 DOM 元素超過 1500 至 2000 個的時候,頁面就開始又延遲,尤其是在小型的、性能差的設備上尤為明顯。
想象一下,有一個無線滾動的頁面,你不斷的下拉,它實際上可能形成瞭上萬個 DOM 元素,每個元素還包含子節點,這樣將消耗巨大的性能。
Virtual scrollers 正是來解決這個問題的。
如上圖,已經表示的很清楚瞭。列表分為可見區域和緩沖區域,超出這個范圍的列表 DOM 都將被刪除。
好啦,準備工作已就緒,Let`s get it!
實現
// imports.js(EndlessList.vue)
import axios from 'axios'; import lodash from 'lodash'; import VirtualList from 'vue-virtual-scroll-list'; import SearchResult from './SearchResult'; import Loader from './Loader'; export default { name: 'EndlessList', components: { VirtualList, Loader }, data() { return { searchQuery: '', currentPage: 0, results: [], itemComponent: SearchResult, loading: false } }, };
我們引入第三方庫 axios 和 loadsh,以便後續使用。
其中,itemComponent 是 virtual-list 的屬性,為此我們需要新建一個 SearchResult 子組件,作為搜索結果單元。
代碼如下:
// SearchResult.vue
<template> <div class="list-item"> <h3> {{ source.title }} </h3> <div v-html="source.snippet"></div> </div> </template> <script> export default { props: { index: { // index of current item type: Number, }, source: { type: Object, default() { return {}; }, }, }, }; </script> <style scoped> .list-item { padding: 0 10px 20px 10px; } .list-item h3 { margin: 0; padding-bottom: 10px; } </style>
我們可以通過搜索標題或描述來得到結果,請求數據來源於維基百科。
search(query, page) { // We prepare the data that the Wikipedia API expects. const data = { action: "query", format: "json", list: "search", continue: "-||", utf8: 1, srsearch: query, sroffset: page * 10, origin: "*", }; // And then we convert these params TO GET params in the format // action=query&format=json ... const params = Object.keys(data) .map(function(k) { return data[k] == "" ? "" : encodeURIComponent(k) + "=" + encodeURIComponent(data[k]); }) .join("&"); // We prepare the url with the params string const searchUrl = `https://en.wikipedia.org/w/api.php?${params}`; // we set loading to true so that we can display the loader this.loading = true; // Then we execute the request and concatenate the results axios.get(searchUrl).then((response) => { this.results = this.results.concat(response.data.query.search); // And of course set loading to false to hide the loader. this.loading = false; }); }
搜索的方法已經寫好,接著就是調用。
- 當用戶鍵入內容的搜索時候會調用。
- 當下拉的時候會調用。
// EndlessList.vue
<script> export default { // data() and methods skipped for brevity watch: { searchQuery: { immediate: true, handler: lodash.debounce(function (newVal) { if (newVal == "") { return; } this.results = []; this.currentPage = 0; this.search(newVal, this.currentPage); this.search(newVal, this.currentPage + 1); this.currentPage = 2; }, 200), }, }, mounted() { const vm = this; window.onscroll = lodash.debounce(function () { var distanceFromBottom = document.body.scrollHeight - window.innerHeight - window.scrollY; if (distanceFromBottom < 400 && vm.searchQuery !== "") { vm.search(vm.searchQuery, vm.currentPage); vm.currentPage++; } }, 100, {leading: true}); }, } </script>
顯而易見,當 searchQuery 變化的時候,我們會得到新的搜索結果。當然,這裡的輸入框也用到瞭防抖函數。
另一個需要註意的是,我們第一次搜索加載瞭兩頁的結果,用戶就會有一定的滾動空間,這樣就可以保持順暢的感覺。
我們在滾動的事件中也加瞭防抖函數。這裡設一個疑問:為什麼要在 window.onscroll 事件下設置 leading 為 true ?
然後我們運行程序看效果:
npm run dev
如何?隻要你不是瘋狂下拉,基本上感受不到 loading 的過程~
小結
用戶不會希望每下拉十條結果就要等待新的十條結果加載出來!所以我們需要有緩沖區,還未下拉到底的時候就預判它到底然後提前加載。這便是絲滑體驗的內核。
當然不在視圖區和緩沖區的 DOM 都將被刪除,這也是頁面不形成大量 DOM 元素的精髓。
這樣動態的處理列表的確是編程人員的一種智慧和用心。
你可以把 項目 克隆到本地再體會一下。以上便是本次分享~
以上就是Vue 虛擬列表的實現示例的詳細內容,更多關於Vue 虛擬列表的資料請關註WalkonNet其它相關文章!