處理陣列方法的弊端
Vue 在響應式的處理中,對陣列與物件採用了不同的方式,如下原始碼所示:
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
複製程式碼
當值是陣列時,Vue 通過攔截陣列變異方法的方式來實現響應式,此種方式有兩弊端:
- 通過索引設定項,Vue 不能監測到。
- 修改陣列長度時,Vue 也不能監測到。
使用與處理純物件相同的方式
既然在單獨處理陣列時,有以上弊端,那為什麼不使用和純物件一樣的方式?
修改部分原始碼如下:
if (Array.isArray(value)) {
// const augment = hasProto
// ? protoAugment
// : copyAugment
// augment(value, arrayMethods, arrayKeys)
// this.observeArray(value)
this.walk(value)
} else {
this.walk(value)
}
複製程式碼
接著,我們主要對陣列測試兩點,利用索引設定項,以及修改陣列長度:
<div id="app">
<div>{{ test }}</div>
<div>{{ test.length }}</div>
<button @click="someMethod">button</button>
</div>
<script>
new Vue({
el: `#app`,
data: {
test: [1, 2, 3, 4]
},
methods: {
someMethod () {
this.test[0] = 5
this.test.length = 10
console.log(this.test) // [5, 2, 3, 4, empty * 6]
}
}
})
</script>
複製程式碼
當點選 button 時,能看到結果:
Wait, 為什麼陣列裡出現了 null ?
null?empty?
當給陣列設定 length 時,如果大於陣列本身長度,新元素則會以 empty 填充,如下所示:
const arr = [1, 2, 3]
arr.length = 5
console.log(arr) // [1, 2, 3, empty * 2]
複製程式碼
empty 不同於 undefined,在遍歷時,會被忽略:
const arr = [1, 2, 3]
arr[5] = undefined
console.log(arr) // [1, 2, 3, empty * 2, undefined]
arr.forEach(item => console.log(item))
// 1 2 3 undefined
複製程式碼
那麼問題來了,上圖中為什麼出現 null?(this.test
列印出來正常,在 html 中渲染出 null)
為了探究此問題,我嘗試在 html 中輸出一個陣列變數:
const arr = [1, 2, 3]
document.write(arr)
複製程式碼
可是事與願違:
我好像得到了字串。
換個物件試試:
const obj = { a: 1 }
document.write(obj)
複製程式碼
結果:
輸出的結果,好像被 toString()
了?
const obj = { a: 1 }
console.log(obj.toString()) // [object Object]
複製程式碼
也就是說,當你嘗試在頁面輸出一個變數時,JavaScript 會自動呼叫 toString()
方法。
既然這樣,為了讓頁面輸出一個變數,需要把變數序列化:
const arr = [1, 2, 3]
arr.length = 6
document.write(JSON.stringify(arr))
複製程式碼
得到結果:
[1, 2, 3, null, null, null]
複製程式碼
陣列成員裡的 empty
在經過 JSON.stringify
後,轉化成了 null
。
大陣列下的效能問題
從例子中可以看出,其實 Vue 是可以使用與處理純物件的方式來處理陣列的。官方解釋不這麼做的原因是出於對效能的考慮。
為了得到驗證,我嘗試使用以下兩種不同方式:
- Vue 單獨處理陣列的方式;
- 和處理純物件相同的方式。
通過兩者頁面 Load 時間,來對比效能差異。
測試程式碼:
<div id="app">
<div>{{ test }}</div>
</div>
<script>
const arr = new Array(100000)
new Vue({
el: `#app`,
data: {
test: arr
}
})
</script>
複製程式碼
當使用 Vue 單獨處理陣列的方式時:
當使用與處理純物件相同的方式時:
可見效能上,前者還是好很多。畢竟遍歷很長的陣列,確實是一件很耗效能的事。
我的部落格即將同步至騰訊雲+社群,邀請大家一同入駐:cloud.tencent.com/developer/s…