分別使用 Vue2 和 Vue3 建立一個元件,用一個物件陣列作為元件的狀態,以它的長度作為變數,考察 Vue2 和 Vue3 效能。
記憶體佔用
陣列長度 | Vue2 | Vue3 |
---|---|---|
1 | 10.2 MB | 11.1 MB |
10 000 | 17.9 MB | 12.1 MB |
100 000 | 67.4 MB | 14.4 MB |
1 000 000 | 568 MB | 36.0 MB |
初始化時間
陣列長度 | Vue2 | Vue3 |
---|---|---|
1 | 7.2 ms | 7.8 ms |
10 000 | 110 ms | 6.9 ms |
100 000 | 803 ms | 6.7 ms |
1 000 000 | 2282 ms | 7.0 ms |
測試程式碼
Vue2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">{{ message }}</div>
<script>
const len = 1000000
const arr = new Array(len)
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: 'test' }
}
console.time('vue2:')
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue!',
arr
}
},
created() {
console.timeEnd('vue2:')
}
})
</script>
</body>
</html>
Vue3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">{{ message }}</div>
<script>
const { createApp } = Vue
const len = 1000000
const arr = new Array(len)
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: 'test' }
}
console.time('vue3:')
createApp({
data() {
return {
message: 'Hello Vue!',
arr
}
},
created() {
console.timeEnd('vue3:')
}
}).mount('#app')
</script>
</body>
</html>
推測
Vue2 使用Object.defineProperty()
建立響應式屬性,初始化時,除了遍歷props
和data
的每個屬性,還會深度遍歷子物件和子陣列,重新定義它們的屬性,並建立如Observer
和Dep
例項,來觀察屬性變化,並且Object.defineProperty()
重寫的get
和set
方法也是掛載在例項上。深度遍歷加上建立這些例項的花銷遠大於原始物件,於是,可以看到 Vue2 無論是記憶體,還是初始化時間隨著陣列長度增加猛漲,和 Vue3 產生了明顯的差距。
當陣列長度到達 100 萬,Vue3 記憶體僅增加 25 MB,但 Vue2 記憶體暴增了 500+ MB,這些多出來的記憶體便是建立這些例項產生的額外開銷。觀察 Vue2 記憶體快照,發現 Vue2 建立了 100 萬個Observer
例項,相當於每個物件一個(另外三個Observer
例項分別用於觀察元件props
、data
和陣列本身)。陣列中每個物件有兩個屬性,相應建立 200 萬個Dep
例項,100 萬個Observer
例項相應建立了100 萬個Dep
例項,共建立超過 300 萬個Dep
例項。
初始化時間來看,Vue3 幾乎不受陣列長度影響,而 Vue2 則隨陣列長度明顯變長。這是因為 Vue3 使用Proxy
類實現響應式,系統會代理響應式物件的操作,如元件的props
和data
,包括屬性讀取、賦值等,不會進行深度遍歷。當讀取響應式物件的屬性時,才會建立Dep
例項,如果讀取的屬性值也是物件時,再將這個物件響應式化,可以說 Vue3 是“懶”響應式。因此,初始化過程中 Vue3 執行時間幾乎不變,如果不去操作這些屬性,也幾乎不會有太多記憶體消耗。
列表渲染
分別使用 Vue2 和 Vue3 渲染 50000 個元素:
<ul>
<li v-for="item in arr">{{ item.id }}</li>
</ul>
可以看出,無論是記憶體佔用或渲染完成時間,Vue3 更佔優勢:
Vue2 | Vue3 | |
---|---|---|
記憶體 | 124 MB | 53.1 MB |
渲染完成時間 | 862ms | 284 ms |