Vue.js 無限滾動列表性能優化方案

問題

你們都知道,Web 頁面修改 DOM 是開銷較大的操做,相比其餘操做要慢不少。這是爲何呢?由於每次 DOM 修改,瀏覽器每每須要從新計算元素佈局,再從新渲染。也就是所謂的重排(reflow)和重繪(repaint)。尤爲是在頁面包含大量元素和複雜佈局的狀況下,性能會受到影響。那對用戶有什麼實際的影響呢?css

一個常見的場景是大數據量的列表渲染。一般表現爲可無限滾動的無序列表或者表格,當數據不少時,頁面會出現明顯的滾動卡頓,嚴重影響了用戶體驗。怎麼解決呢?vue

解決方案

既然問題的根源是 DOM 元素太多,那就想辦法限制元素數量。git

  • 限制列表對用戶可見的元素數量。咱們把可見區域稱爲 ViewPort
  • 當列表滾動時,列表種的其餘元素怎麼由不可見變爲可見?
  • 監聽列表容器元素的滾動事件,當列表裏的元素進入可視區域,則添加到DOM中
  • 問題是若是一直這麼滾下去,列表會愈來愈大。因此須要在列表元素離開 ViewPort 的時候從DOM中移除
  • 問題又來了,因爲 ViewPort 恰好是一屏的大小,滾動的時候元素還沒來得及渲染,會出現一段時間的空白。解決辦法就是上下增長一部分數據渲染。
    無限滾動

無限滾動的性能優化方案基本思路就是這樣。github

在實際項目中,咱們可能不須要本身從頭實現一個無限滾動列表組件,Vue.js 就有一個現成的輪子:vue-virtual-scrollernpm

在項目中安裝這個插件:json

$ npm install -D vue-virtual-scroller
複製代碼

項目入口文件 main.js 引入這個插件:瀏覽器

import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
import Vue from "vue";
import VueVirtualScroller from "vue-virtual-scroller";

Vue.use(VueVirtualScroller);
複製代碼

案例一:VirtualList

咱們來看一個簡單的例子,用vue-virtual-scroller渲染一個包含大量數據的列表。 先用 JSON-Generator 生成 5000 條數據的 JSON 對象,並保存到 data.json 文件。能夠用下面的規則:性能優化

[
  '{{repeat(5000)}}',
  {
    _id: '{{objectId()}}',
    age: '{{integer(20, 40)}}',
    name: '{{firstName()}} {{surname()}}',
    company: '{{company().toUpperCase()}}'
  }
]

複製代碼

新建一個 VirtualList.vue 文件,引入data.json,並將它賦值給組件的items屬性。而後套一個 <virtual-scroller>組件:bash

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 組件必須設置 item-height 。另外,因爲咱們要建立一個列表,能夠設置content-tag="ul",表示內容渲染成 <ul>標籤。

vue-virtual-scroller 支持使用 scoped slots,增長了內容渲染的靈活性。經過使用slot-scope="props",咱們能夠訪問 vue-virtual-scroller 暴露的數據。

props 有一個itemKey屬性,出於性能考慮,咱們應該在內容部分的根元素上綁定 :key="props.itemKey"。而後咱們就能夠經過 props.item 拿到 JSON 裏的原始數據了。

若是你要給列表設置樣式,能夠給 virtual-scroller 設置 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,咱們再看一個表格組件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>

複製代碼

這裏有個小問題,咱們須要增長一個 <thead>標籤,用於顯示列名: AgeName 和 Company

幸虧 virtual-scroller 支持 slot,能夠定製各部份內容:

<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 均可以放置自定義內容。container 會被 container-tag 屬性值替換,默認是divcontent 被 content-tag 值替換。

這裏用 before-content slot 加一個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>

複製代碼

請注意,咱們把content-tag="table" 改爲了content-tag="tbody",由於咱們設置了container-tag="table",這是爲了構造table 標籤的常規結構。

若是要加一個 tfoot,應該知道怎麼作了吧。

總結

咱們瞭解了無限滾動列表的性能優化原理,以及利用vue-virtual-scroller Vue 插件建立了 VirtualList 和  VirtualTable 組件。若是用它們來展現前面生成的 5000 條數據,應該能夠比較流暢地渲染和滾動了。更多用法能夠參考 vue-virtual-scroller 文檔 。

文章涉及的源碼 戳這裏。更多技術乾貨,歡迎關注個人微信公衆號:1024譯站。

微信公衆號:1024譯站
相關文章
相關標籤/搜索