先説結論
- dom 節點數量對內存影響沒想象中大,js 變量才是內存佔用的元兇
- 內存佔用及運行性能對比:原生 js < v-for div ≈ v-for 函數式組件 < v-for 普通組件
- 去虛擬 dom 化框架正在崛起,成為一種新的選擇
dom 節點數量對內存影響沒想象中大(十萬 div 僅佔用 400mb)
- 測試示例
- 生成 100,000(十萬)個 div,內存佔用僅 400mb,平均每個 div 佔用內存 400 * 1024 kb / 100,000 = 4kb
- 注意,這裏內存佔用是靜態狀態內存,即 div 生成完成後等待一會(約1min)內存回收完成後的內存佔用,div 生成過程中會產生中間變量,內存佔用會比靜態時高。
- 如果短時間多少清空 div 再重新生成,會導致內存佔用變大
組件過多,或許才是 vue 項目內存佔用大、運行慢的原因
分別以「純 js 渲染 div」、「v-for 渲染 div」、「v-for 渲染函數式組件」、「v-for 渲染普通組件」為例做性能對比測試
「純 js 渲染 div」運行示例,對應源碼
-
10,000(一萬)個 div
- 渲染耗時:26ms,平均單個div渲染耗時:0.0026ms
- 渲染耗時:26ms,平均單個div渲染耗時:0.0026ms
-
100,000(十萬)個 div
- 渲染耗時:265ms,平均單個div渲染耗時:0.0027ms
- 渲染耗時:265ms,平均單個div渲染耗時:0.0027ms
-
100,000(十萬)個 div
- 佔用內存:528mb,平均單個佔用內存:528 * 1024 / 100,000 = 5.4kb
- 佔用內存:528mb,平均單個佔用內存:528 * 1024 / 100,000 = 5.4kb
「v-for 渲染 div」運行示例,對應源碼
-
10,000(一萬)個 div
- 使用 key 緩存節點渲染耗時:61ms,平均單個div渲染耗時:0.0061ms
- 不使用 key 緩存節點渲染耗時:91ms,平均單個div渲染耗時:0.0091ms
-
100,000(十萬)個 div
- 使用 key 緩存節點渲染耗時:499ms,平均單個div渲染耗時:0.005ms
- 不使用 key 緩存節點渲染耗時:286ms,平均單個div渲染耗時:0.0029ms
-
100,000(十萬)個 div
- 佔用內存:586mb,平均單個佔用內存:586 * 1024 / 100,000 = 6kb
- 佔用內存:586mb,平均單個佔用內存:586 * 1024 / 100,000 = 6kb
「v-for 渲染函數式組件」運行示例,對應源碼
-
10,000(一萬)個 div
- 使用 key 緩存節點渲染耗時:85ms,平均單個div渲染耗時:0.0085ms
- 不使用 key 緩存節點渲染耗時:82ms,平均單個div渲染耗時:0.0082ms
-
100,000(十萬)個 div
- 使用 key 緩存節點渲染耗時:392ms,平均單個div渲染耗時:0.0039ms
- 不使用 key 緩存節點渲染耗時:505ms,平均單個div渲染耗時:0.005ms
-
100,000(十萬)個 div
- 佔用內存:654mb,平均單個佔用內存:654 * 1024 / 100,000 = 6.7kb
- 佔用內存:654mb,平均單個佔用內存:654 * 1024 / 100,000 = 6.7kb
「v-for 渲染普通組件」運行示例,對應源碼
-
10,000(一萬)個 div
- 使用 key 緩存節點渲染耗時:122ms,平均單個div渲染耗時:0.0122ms
- 不使用 key 緩存節點渲染耗時:360ms,平均單個div渲染耗時:0.0325ms
-
100,000(十萬)個 div
- 使用 key 緩存節點渲染耗時:918ms,平均單個div渲染耗時:0.0092ms
- 不使用 key 緩存節點渲染耗時:13881ms(13.8s),平均單個div渲染耗時:0.1388ms
-
100,000(十萬)個 div
- 佔用內存:907mb,平均單個佔用內存:907 * 1024 / 100,000 = 9.3kb
- 佔用內存:907mb,平均單個佔用內存:907 * 1024 / 100,000 = 9.3kb
通過上述測試可得
| 純 js 渲染 | v-for 渲染 div | v-for 渲染函數式組件 | v-for 渲染普通組件 | |
|---|---|---|---|---|
| 一萬div複用key渲染耗時 | - | 61ms/6.1us | 85ms/8.5us | 122ms/12.2us |
| 一萬div不復用key渲染耗時 | 26ms/2.6us | 91ms/9.1us | 82ms/8.2us | 360ms/36us |
| 十萬div複用key渲染耗時 | - | 286ms/2.9us | 392ms/3.9ms | 918ms/9.2us |
| 十萬div不復用key渲染耗時 | 265ms/2.7us | 499m/5us | 505ms/5us | 13881ms/138.8us |
| 十萬div內存佔用 | 528mb/5.4kb | 586mb/6kb | 652mb/6.7kb | 907mb/9.3kb |
- dom 節點數量對內存影響沒想象中大,10萬 div 佔用內存 400mb(只生成一次),平均單個 div 佔用內存僅 4kb
- vue 組件對內存佔用較大,僅僅最簡單的 div 組件就需要額外添加 9.3 - 5.4 = 3.9kb 內存,若組件複雜度上升,則內存佔用將進一步增加。成為內存瓶頸
- vue 函數式組件、v-for div 是否使用 key 緩存節點,對運行耗時變化不大
- vue 組件是 key 緩存節點,對節點更新性能有很大提升,10萬組件更新,從 13s 耗時優化到 0.9s 耗時,減少近 13 - 0.9 / 13 = 93% 運算耗時
- vue 組件並非越細越好,顆粒度太細,將導致 vue 組件數量急劇增加,內存佔用急劇增加
- 純 js 渲染 div 最快,使用 v-for div,v-for 函數式組件的渲染耗時至少是 純 js 渲染耗時的兩倍,這是由於虛擬節點 vnode 需要執行 diff 算法決定的。
去虛擬化正在崛起
既然 vnode 計算如此消耗性能,那可否捨棄 vnode 來提高性能呢?答案是肯定的,svelte(類似 vue),solid(類似 react) 就是這麼做的
Svelte,新興前端框架 Svelte 從入門到原理,Virtual DOM is pure overhead,虛擬DOM是純粹的浪費
Solidjs,SolidJS入門