
說好的虛擬呢?記憶體爆炸啦!Σ(ˊДˋ;)
某個專案回報網頁會越來越卡,甚至崩潰。
查了一下是因為記憶體用量像鱈魚的體重一樣越來越高,都不知道是鯨魚還是鱈魚了。
鱈魚:「旁白不要給我亂念劇本 ლ(´ 口`ლ)」
嘗試重現了一下,發現在虛擬滾動列表中,反覆滾動會讓記憶體不斷上升,而且還沒有辦法釋放。
讓我們來看看是怎麼回事。
虛擬滾動
若頁面一次載入太多元素,可能會導致頁面卡頓,甚至崩潰。
這時候我們可以透過虛擬滾動來解決這個問題。
虛擬滾動只會顯示可見區域的元素,同時也會預先載入(Overscan)可見區域外的一些元素,避免快速捲動時出現大量空白。
下方是一個虛擬滾動的互動說明,可以清楚看到可視區域、渲染區域和未渲染區域。
UI 套件可能會有內建虛擬滾動元件,如果沒有、需求簡單,則可以使用 VueUse 的 useVirtualList。
使用上非常簡單:
<template>
<div
v-bind="containerProps"
class="overflow-auto p-2 border border-gray-400/80 rounded opacity-80"
>
<div v-bind="wrapperProps">
<div
v-for="item in list"
:key="item.index"
class="mb-2 flex items-center justify-center h-[40px] bg-gray-200/50"
>
Row {{ item.index }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useVirtualList } from '@vueuse/core'
const allItems = Array
.from({ length: 500 })
.map((_, index) => ({ index, value: `${index}` }))
const { list, containerProps, wrapperProps } = useVirtualList(
allItems,
{ itemHeight: 40 },
)
</script>結果如下:
問題重現
不難注意到虛擬列表會不斷建立、銷毀 DOM 元素,如果我們反覆滾動,記憶體就會不斷上升。
讓我們快速滾動列表並打開 DevTools 使用 Performance Monitor(效能監視器)查看記憶體與 DOM 的變化。

可以注意到即使 DOM 元素一度衝到接近 40000 個,也會在特定時間點釋放並不會無限上升。
所以到底是怎麼回事呢?(́⊙◞౪◟⊙‵)
反覆交叉嘗試後,發現問題出在 input 元素,沒錯,就是那個樸實無華的輸入框。(◉◞౪◟◉ )
只要在剛剛的虛擬列表中加入 input 元素,記憶體就會不斷上升,不會自動釋放,直到記憶體爆炸。
<template>
<div
v-bind="containerProps"
class="overflow-auto p-2 border border-gray-400/80 rounded opacity-80"
>
<div v-bind="wrapperProps">
<div
v-for="item in list"
:key="item.index"
class="mb-2 flex items-center justify-center h-[40px] bg-gray-200/50"
>
Row {{ item.index }}
<input
v-model="value"
class=" outline ml-4"
>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useVirtualList } from '@vueuse/core'
import { ref } from 'vue'
const allItems = Array
.from({ length: 99999 })
.map((_, index) => ({ value: `${index}` }))
const { list, containerProps, wrapperProps } = useVirtualList(
allItems,
{ itemHeight: 40 },
)
const value = ref('codfish')
</script>一樣不斷滾動列表,並查看 Performance Monitor。

可以看到 DOM 數量不斷上升,完全沒有釋放的跡象。(;´༎ຶД༎ຶ`)
鱈魚:「真相出爐了,兇手就是 input!ヽ(●`∀´●)ノ」
同事:「可是一樣的範例,我的電腦沒有這個問題欸(´・ω・`)」
鱈魚:...ヽ(́⊙◞౪◟⊙‵)ノ
使用不同瀏覽器交叉測試後,發現某些瀏覽器外掛會導致問題(例如:密碼管理工具),關閉外掛後就沒問題了,所以該怎麼辦呢?
鱈魚:「這個簡單,關閉外掛或只能用乾淨瀏覽器,不要拉倒 (・∀・)9」
同事:「PM 在你後面,他非常火 ╭(°A ,°`)╮」
我們沒辦法控制客戶的瀏覽器環境,只能調整 input 出現的時機了。
可以改成開啟編輯模式時才顯示 input 或使用像 Popup Edit 這種點擊後顯示浮動 input 的元件。
最後改成 Popup Edit 方案,不但問題解決惹,性能也提升了一點。ԅ( ˘ω˘ԅ)
總結 🐟
- 虛擬列表可以確保大量資料顯示時,不會造成頁面卡頓。
- 某些瀏覽器外掛會導致問題,調整
input出現的時機,避免大量建立、銷毀input元素。