大數據量場景下的Vue性能優化

性能優化最多見的落腳點是在網絡和dom上,可是在大數據量的場景下,因爲Vue自己的特性,可能會形成js運行層面的性能問題,這篇文章討論的就是針對這一部分的性能優化方案。javascript

模擬一個大數據量的場景

// App.vue
<template>
    <div>
        <p>It's {{ firstUser.name }}'s show time</p>
        <div>total: {{ total }}</div>
    </div>
</template>

<script> const user = [] let i = 0 while (i++ < 50000) { user.push({ id: i, age: 18, name: `kunkun_${i}`, alais: 'Irving', gender: 'female', education: 'senior high school', height: 'xxx', weight: 'xxx', hobby: 'xxx', tag: 'xxx', skill: { sing: 0, dance: 0, rap: 0, basketball: 100, }, }) } export default { data: { userList: user, }, computed: { firstUser() { const userList = this.userList return userList.length ? userList[0] : {} }, total() { return this.userList.length }, }, } 複製代碼

如以上代碼所示,模擬了5萬個用戶,每個用戶擁有id, name, age等等字段。 jsfiddlehtml

打開chrome devtool的Performance工具,能夠看到,渲染這個組件的過程當中,Observer這個階段耗時2.19s(測試機器配置爲桌面端i7,16g內存)。 vue

未優化.png

接下來,我會一步一步的把這一段耗時減小到10ms。java

分析緣由

衆所周知,Vue在渲染組件的時候,會對data對象進行改造,遍歷data的key,調用defineProperty方法定義它的setter和getter。若是某個字段是Object,或者Array,還會遞歸的對這個字段進行上訴操做。chrome

一般狀況下,這個操做耗時是很短的,可是當數據量很是大的時候,對每個數據項的每個字段都進行defineProperty的操做就是一個昂貴的操做,因此性能優化的出發點就是減小defineProperty的次數。api

Step 1, 減小無用字段

在這個模擬的例子當中,其實我只須要2個字段,一個是name,一個是id(id甚至也能夠不要),因此我把多餘的字段都去掉,一共減小了8個String類型的字段,和一個Object類型的字段,能夠減小 (8 + 4) * n次defineProperty操做和n次遞歸調用。看看結果如何。 數組

去除無用字段.png

Observer這個操做從2.2s減小到了515ms,提高仍是比較大的。性能優化

Step2,數據扁平化

在當前版本(2.x)的Vue當中,對於數據變更的檢測有許多限制,好比不能檢測對象屬性的添加和刪除;不能檢測到經過數據索引直接設置數據項等等。markdown

因此,當一個數組的數據項都是基本數據類型的時候,Vue不會進行任何操做網絡

首先,把user數據拍扁

const user = []
let i = 0
while (i++ < 50000) { user.push(`kun_${i}`, i) // 經過index爲基數仍是偶數分辨是name仍是id } 複製代碼

而後,相應的改變computed的計算方法,不影響渲染邏輯和業務邏輯

...
computed: {
    firstUser() {
        const userList = this.userList
        return userList.length ? { name: userList[0], id: userList[1] } : {}
    },

    total() {
        return this.userList.length / 2
    },
}
...
複製代碼

jsfiddle - 數據扁平化

看看結果如何

扁平數據.png
從上圖能夠看出,結果很是的明顯,從515ms直接減小到了7ms,幾乎徹底避免了性能損耗。

Step3,利用computed

到此爲止,性能上的問題已經解決了,可是扁平的數據會影響業務代碼的開發效率和可讀性,同時數據和它的index產生了深耦合,若是咱們須要添加一個字段使用或者改變下順序,很容易出問題。 不過,咱們能夠利用computed計算屬性把已經被拍扁的數據從新組裝起來。因爲Vue的響應式數據改造只針對data選項和props選項,不包括computed,因此只會產生不多的函數運行耗時。

export default {
    data() {
        return {
            // 扁平的數據存起來
            originSserList: user,
        }
    },

    computed: {
        firstUser() {
            const userList = this.userList
            return userList.length ? userList[0] : {}
        },

        total() {
            return this.userList.length
        },

        // 從新'組裝'便於使用的計算屬性,不影響本來的渲染和業務邏輯
        userList() {
            const result = []
            const user = this.originSserList
            for(let i = 0; i < user.length; i += 2) {
                const name = user[i]
                const id = user[i + 1]
                result.push({ name, id })
            }
            return result
        },
    },
}
複製代碼

看看這種狀況下的Performance。

computed+扁平.png
僅僅只是多出了10ms的函數運行時間。

到這裏,在無需改動任何的渲染邏輯和業務邏輯的狀況下,將js的運行時間從2.2s減小到了10ms左右,提高了200倍。而且這些數據是在桌面端i7處理器下獲得的,大大超越了絕大部分的用戶的機器性能,更不用說移動端了,因此在實際的大數據量場景下,能取得更加明顯的用戶可感知的性能提高。

jsfiddle - 數據扁平化 + computed

Step4,數據靜態化

沒想到吧,還有Step4?已經沒有優化空間了呀。

在這個模擬的場景裏面,確實沒有優化的空間了,不過,並非全部的數據均可以很好的進行扁平化處理,這可能涉及到方方面面的緣由與權衡。那麼這種狀況下,如何進行優化呢?

一般在Vue組件當中,都是把數據放在data選項當中,Vue會對data選項中的數據進行響應式改造,我稱之"動態數據"或者"響應式"數據,可是並非全部的數據都是會發生變化的,不少時候,特別是大數據量場景下的數據是不會或者不多發生變化的,這種狀況下,就沒有必要把它放到data選項中去,而是在beforeCreated當中進行數據初始化,也不會影響數據的使用。

beforeCreated() {
    this.userList = xxx // 記得把data當中的userList刪掉
}
複製代碼

這種處理方式,我稱之爲數據靜態化,這種數據,我稱之爲"靜態數據"

可是,有一點須要特別的注意,靜態數據並不在Vue的響應式系統當中,也就是說當你進行this.userList = newUserList時,視圖不會從新渲染,對應的computed計算屬性也不會從新計算。沒有了Vue提供的響應式系統,若是數據變更的時候,咱們須要手動的去計算對應的數據,可能還須要配合$forceUpdate這個api去從新渲染視圖。此時,須要在性能和代碼可讀性與開發效率上進行取捨與權衡。

總結

因爲Vue的響應式系統,大數據量場景下可能會形成js運行層面的性能問題,能夠經過3個方法去解決

  • 減小無用字段
  • 數據扁平化
  • 數據靜態化

這3個方法相互並不衝突,能夠根據實際狀況選擇其中的1種或多種方法進行組合。

相關文章
相關標籤/搜索