Vue 虛擬列表的實(shí)戰(zhàn)示例
現(xiàn)如今,我們總是在無止境的刷。刷微博、刷抖音、刷沸點(diǎn)......一次次絲滑下拉體驗(yàn)的背后卻是前端攻城獅的用心。
本篇討論基于 Vue.js 的列表無限下拉實(shí)踐。
我們的目標(biāo)就是:讓列表下拉縱享絲滑,而不是像以往的下拉就 loading 等待的體驗(yàn)。
譯自 Better Programming 在線 Demo設(shè)計(jì)咱還是用 Vue CLI 來快速構(gòu)建項(xiàng)目。
這是主頁面:
// EndlessList.vue
<template> <div class='endless-scrolling-list'> <!-- 搜索框 --> <div class='search-box'> <input type='text' v-model='searchQuery'/> </div> <p 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>
其中核心當(dāng)然是virtual-list組件啦~
這里的虛擬列表,我們用到一個(gè)三方庫 Vue Virtual Scroll List,它在 Github 上又 2.5k+ 的 stars。類比于 react 的 react-virtualized 庫。
大量的 DOM 元素會使得我們的網(wǎng)頁非常“重”。當(dāng) DOM 元素超過 1500 至 2000 個(gè)的時(shí)候,頁面就開始又延遲,尤其是在小型的、性能差的設(shè)備上尤為明顯。
想象一下,有一個(gè)無線滾動(dòng)的頁面,你不斷的下拉,它實(shí)際上可能形成了上萬個(gè) DOM 元素,每個(gè)元素還包含子節(jié)點(diǎn),這樣將消耗巨大的性能。
Virtual scrollers 正是來解決這個(gè)問題的。
如上圖,已經(jīng)表示的很清楚了。列表分為可見區(qū)域和緩沖區(qū)域,超出這個(gè)范圍的列表 DOM 都將被刪除。
好啦,準(zhǔn)備工作已就緒,Let`s get it!
實(shí)現(xiàn)// 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,以便后續(xù)使用。
其中,itemComponent 是 virtual-list 的屬性,為此我們需要新建一個(gè) SearchResult 子組件,作為搜索結(jié)果單元。
代碼如下:
// 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>
我們可以通過搜索標(biāo)題或描述來得到結(jié)果,請求數(shù)據(jù)來源于維基百科。
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; });}
搜索的方法已經(jīng)寫好,接著就是調(diào)用。
當(dāng)用戶鍵入內(nèi)容的搜索時(shí)候會調(diào)用。 當(dāng)下拉的時(shí)候會調(diào)用。// 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>
顯而易見,當(dāng) searchQuery 變化的時(shí)候,我們會得到新的搜索結(jié)果。當(dāng)然,這里的輸入框也用到了防抖函數(shù)。
另一個(gè)需要注意的是,我們第一次搜索加載了兩頁的結(jié)果,用戶就會有一定的滾動(dòng)空間,這樣就可以保持順暢的感覺。
我們在滾動(dòng)的事件中也加了防抖函數(shù)。這里設(shè)一個(gè)疑問:為什么要在 window.onscroll 事件下設(shè)置 leading 為 true ?
然后我們運(yùn)行程序看效果:
npm run dev
如何?只要你不是瘋狂下拉,基本上感受不到 loading 的過程~
小結(jié)用戶不會希望每下拉十條結(jié)果就要等待新的十條結(jié)果加載出來!所以我們需要有緩沖區(qū),還未下拉到底的時(shí)候就預(yù)判它到底然后提前加載。這便是絲滑體驗(yàn)的內(nèi)核。
當(dāng)然不在視圖區(qū)和緩沖區(qū)的 DOM 都將被刪除,這也是頁面不形成大量 DOM 元素的精髓。
這樣動(dòng)態(tài)的處理列表的確是編程人員的一種智慧和用心。
你可以把 項(xiàng)目 克隆到本地再體會一下。以上便是本次分享~
以上就是Vue 虛擬列表的實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于Vue 虛擬列表的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. js select支持手動(dòng)輸入功能實(shí)現(xiàn)代碼2. asp.net core項(xiàng)目授權(quán)流程詳解3. CSS3中Transition屬性詳解以及示例分享4. jsp文件下載功能實(shí)現(xiàn)代碼5. 開發(fā)效率翻倍的Web API使用技巧6. bootstrap select2 動(dòng)態(tài)從后臺Ajax動(dòng)態(tài)獲取數(shù)據(jù)的代碼7. vue使用moment如何將時(shí)間戳轉(zhuǎn)為標(biāo)準(zhǔn)日期時(shí)間格式8. html中的form不提交(排除)某些input 原創(chuàng)9. PHP橋接模式Bridge Pattern的優(yōu)點(diǎn)與實(shí)現(xiàn)過程10. ASP常用日期格式化函數(shù) FormatDate()
