首先看源码
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})
确实只有7个方法被重写,至于为啥是7个,我观察了一下特征,这7个方法都是能改变数组本身的方法,突然顿悟也只有这个数组本身改变需要检测才需要去重新通知。接着我去MDN查了下,能改变数组本身分方法的还有以下这两个方法
Array.prototype.fill()
Array.prototype.copyWithin()
那为啥vue没有重写这两个方法呢,后来想了下,vue2出生的早,这两个方法es6出生的晚,那时候还不知道兼容这两个方法,我又想不会吧,那现在vue2给一个数组用fill方法,不就监听不到变化?于是我用代码试了下
data() {
return {
arr: [1, 2, 4]
}
test() {
this.arr.fill(0, 0, 2);
},
<el-button @click="test"></el-button>
<div v-for="(item, i) in arr" :key="i"> {{ item }} </div>
果然点了没反应,数组不会重新渲染。
我想尤大肯定知道这个情况,人家就是不愿意改了,觉得没必要兼容,就算你提issue估计也不会改,我想了下一个hack的办法,arr.fill(0, 0, 2)完后再手动调用arr.push(),注意这里push一个空元素,也不会改变数组本身,但是因为vue会监听push方法,所以这才数组就会重新渲染。