Vue.js 無限滾動(dòng)列表性能優(yōu)化方案
問題
大家都知道,Web 頁面修改 DOM 是開銷較大的操作,相比其他操作要慢很多。這是為什么呢?因?yàn)槊看?DOM 修改,瀏覽器往往需要重新計(jì)算元素布局,再重新渲染。也就是所謂的重排(reflow)和重繪(repaint)。尤其是在頁面包含大量元素和復(fù)雜布局的情況下,性能會(huì)受到影響。那對(duì)用戶有什么實(shí)際的影響呢?
一個(gè)常見的場(chǎng)景是大數(shù)據(jù)量的列表渲染。通常表現(xiàn)為可無限滾動(dòng)的無序列表或者表格,當(dāng)數(shù)據(jù)很多時(shí),頁面會(huì)出現(xiàn)明顯的滾動(dòng)卡頓,嚴(yán)重影響了用戶體驗(yàn)。怎么解決呢?
解決方案
既然問題的根源是 DOM 元素太多,那就想辦法限制元素?cái)?shù)量。
- 限制列表對(duì)用戶可見的元素?cái)?shù)量。我們把可見區(qū)域稱為 ViewPort
- 當(dāng)列表滾動(dòng)時(shí),列表種的其他元素怎么由不可見變?yōu)榭梢姡?/li>
- 監(jiān)聽列表容器元素的滾動(dòng)事件,當(dāng)列表里的元素進(jìn)入可視區(qū)域,則添加到DOM中
- 問題是如果一直這么滾下去,列表會(huì)越來越大。所以需要在列表元素離開 ViewPort 的時(shí)候從DOM中移除
- 問題又來了,由于 ViewPort 剛好是一屏的大小,滾動(dòng)的時(shí)候元素還沒來得及渲染,會(huì)出現(xiàn)一段時(shí)間的空白。解決辦法就是上下增加一部分?jǐn)?shù)據(jù)渲染。
無限滾動(dòng)的性能優(yōu)化方案基本思路就是這樣。
在實(shí)際項(xiàng)目中,我們可能不需要自己從頭實(shí)現(xiàn)一個(gè)無限滾動(dòng)列表組件,Vue.js 就有一個(gè)現(xiàn)成的輪子:vue-virtual-scroller。
在項(xiàng)目中安裝這個(gè)插件:
$ npm install -D vue-virtual-scroller
項(xiàng)目入口文件 main.js 引入這個(gè)插件:
import "vue-virtual-scroller/dist/vue-virtual-scroller.css"; import Vue from "vue"; import VueVirtualScroller from "vue-virtual-scroller"; Vue.use(VueVirtualScroller);
案例一:VirtualList
我們來看一個(gè)簡(jiǎn)單的例子,用vue-virtual-scroller渲染一個(gè)包含大量數(shù)據(jù)的列表。 先用JSON-Generator 生成 5000 條數(shù)據(jù)的 JSON 對(duì)象,并保存到 data.json 文件??梢杂孟旅娴囊?guī)則:
[ '{{repeat(5000)}}', { _id: '{{objectId()}}', age: '{{integer(20, 40)}}', name: '{{firstName()}} {{surname()}}', company: '{{company().toUpperCase()}}' } ]
新建一個(gè) VirtualList.vue 文件,引入data.json,并將它賦值給組件的items屬性。然后套一個(gè) <virtual-scroller>組件:
VirtualList.vue:
<template> <virtual-scroller :items="items" item-height="40" content-tag="ul"> <template slot-scope="props"> <li :key="props.itemKey">{{props.item.name}}</li> </template> </virtual-scroller> </template> <script> import items from "./data.json"; export default { data: () => ({ items }) }; </script>
virtual-scroller 組件必須設(shè)置 item-height 。另外,由于我們要?jiǎng)?chuàng)建一個(gè)列表,可以設(shè)置content-tag="ul",表示內(nèi)容渲染成 <ul>標(biāo)簽。
vue-virtual-scroller 支持使用 scoped slots,增加了內(nèi)容渲染的靈活性。通過使用slot-scope="props",我們可以訪問 vue-virtual-scroller 暴露的數(shù)據(jù)。
props 有一個(gè)itemKey屬性,出于性能考慮,我們應(yīng)該在內(nèi)容部分的根元素上綁定 :key="props.itemKey"。然后我們就可以通過 props.item 拿到 JSON 里的原始數(shù)據(jù)了。
如果你要給列表設(shè)置樣式,可以給 virtual-scroller 設(shè)置 class屬性:
<template> <virtual-scroller class="virtual-list" ...></virtual-scroller> </template> <style> .virtual-list ul { list-style: none; } </style>
或者也可以用scoped 樣式,用 /deep/選擇器:
<style scoped> .virtual-list /deep/ ul { list-style: none; } </style>
案例二: VirtualTable
類似 VirtualList,我們?cè)倏匆粋€(gè)表格組件VirtualTable: VirtualTable.vue:
<template> <virtual-scroller :items="items" item-height="40" content-tag="table"> <template slot-scope="props"> <tr :key="props.itemKey"> <td>{{props.item.age}}</td> <td>{{props.item.name}}</td> <td>{{props.item.company}}</td> </tr> </template> </virtual-scroller> </template> <script> import items from "./data.json"; export default { data: () => ({ items }) }; </script>
這里有個(gè)小問題,我們需要增加一個(gè) <thead>標(biāo)簽,用于顯示列名: Age, Name 和 Company
幸好 virtual-scroller 支持 slot,可以定制各部分內(nèi)容:
<main> <slot name="before-container"></slot> <container> <slot name="before-content"></slot> <content> <!-- Your items here --> </content> <slot name="after-content"></slot> </container> <slot name="after-container"></slot> </main>
這些 slot 都可以放置自定義內(nèi)容。container 會(huì)被 container-tag 屬性值替換,默認(rèn)是div,content 被 content-tag 值替換。
這里用 before-content slot 加一個(gè)thead 就行了:
<template> <virtual-scroller :items="items" item-height="40" container-tag="table" content-tag="tbody" > <thead slot="before-content"> <tr> <td>Age</td> <td>Name</td> <td>Company</td> </tr> </thead> <template slot-scope="props"> <tr :key="props.itemKey"> <td>{{props.item.age}}</td> <td>{{props.item.name}}</td> <td>{{props.item.company}}</td> </tr> </template> </virtual-scroller> </template>
請(qǐng)注意,我們把content-tag="table" 改成了content-tag="tbody",因?yàn)槲覀冊(cè)O(shè)置了container-tag="table",這是為了構(gòu)造table 標(biāo)簽的常規(guī)結(jié)構(gòu)。
如果要加一個(gè) tfoot,應(yīng)該知道怎么做了吧。
總結(jié)
我們了解了無限滾動(dòng)列表的性能優(yōu)化原理,以及利用vue-virtual-scroller Vue 插件創(chuàng)建了 VirtualList 和 VirtualTable 組件。如果用它們來展示前面生成的 5000 條數(shù)據(jù),應(yīng)該可以比較流暢地渲染和滾動(dòng)了。更多用法可以參考 vue-virtual-scroller 文檔 。
文章涉及的源碼 戳這里。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue實(shí)現(xiàn)循環(huán)滾動(dòng)列表
- vue實(shí)現(xiàn)列表無縫循環(huán)滾動(dòng)
- vue實(shí)現(xiàn)列表滾動(dòng)的過渡動(dòng)畫
- 基于vue實(shí)現(xiàn)循環(huán)滾動(dòng)列表功能
- vue實(shí)現(xiàn)列表無縫滾動(dòng)效果
- 基于Vue3實(shí)現(xiàn)列表虛擬滾動(dòng)效果
- vue實(shí)現(xiàn)列表固定列滾動(dòng)
- vue實(shí)現(xiàn)列表無縫滾動(dòng)
- Vue3封裝自動(dòng)滾動(dòng)列表指令(含網(wǎng)頁縮放滾動(dòng)問題)
相關(guān)文章
Vuejs對(duì)象常用操作之取對(duì)應(yīng)的值、取key和value值、轉(zhuǎn)數(shù)組及合并等
最近在學(xué)Vue和javascript感覺js的好多方法都不太清楚,這里徹底總結(jié)下,這篇文章主要給大家介紹了關(guān)于Vuejs對(duì)象常用操作之取對(duì)應(yīng)的值、取key和value值、轉(zhuǎn)數(shù)組及合并等的相關(guān)資料,需要的朋友可以參考下2024-01-01使用vue編寫一個(gè)點(diǎn)擊數(shù)字計(jì)時(shí)小游戲
這篇文章主要為大家詳細(xì)介紹了使用vue編寫一個(gè)點(diǎn)擊數(shù)字計(jì)時(shí)小游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08vue修改數(shù)據(jù)視圖更新原理學(xué)習(xí)
這篇文章主要為大家介紹了vue修改數(shù)據(jù)視圖更新原理學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11在vue項(xiàng)目中使用axios發(fā)送post請(qǐng)求出現(xiàn)400錯(cuò)誤的解決
這篇文章主要介紹了在vue項(xiàng)目中使用axios發(fā)送post請(qǐng)求出現(xiàn)400錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09解決ElementUI中tooltip出現(xiàn)無法顯示的問題
這篇文章主要介紹了解決ElementUI中tooltip出現(xiàn)無法顯示的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03