Vue2中检测数组变化
文章类型:Vue
发布者:admin
发布时间:2023-03-19
数组在 JS 中常被当作栈,队列,集合等数据结构的实现方式,会有批量的数据以待遍历。
并且 runtime 对对象与数组的优化也有所不同,
Vue2数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截,
vue 中是通过对每个键设置 getter/setter 来实现响应式的,
Vue 没有对数组每个键设置响应式的过程,而是直接对值进行递归设置响应式,原因是使用数组,目的是遍历,调用 getter 开销太大了,
所以 Vue 不在数组每个键上设置,而是在数组上定义 __ob__
重写 7 种数组(push,shift,pop,splice,unshift,sort,reverse)方法
那么,利用索引修改数组的值和修改数组长度时是无法监控
vue2中提供了3种解决方案,Vue.set、vm.$set、vm.items.splice来实现
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)//末尾添加新元素方式
$set源码解析:
如果target是一个数组且索引有效,就设置length的属性。
通过splice方法把value设置到target数组指定位置。
设置的时候,vue会拦截到target发生变化,然后把新增的value也变成响应式,然后返回value
function set (target, key, val) {
//...
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val
}
//...
defineReactive$$1(ob.value, key, val);
ob.dep.notify();
return val
}
原理:
主要采用函数劫持的方式,重写了数组方法,改变数组的原型,用自己的方法,然后通知视图去更新,数组里每一项可能是对象,会对数组的每一项进行观测,(且只有数组里的对象才能进行观测,观测过的也不会进行观测)
源码分析:
首先时拷贝数组原型->然后继承自己的数组原型->重写原型方法
触发方法时->进行判断是否需要深度监听->手动通知视图更新
// 拿到数组原型拷贝一份
const arrayProto = Array.prototype
// 然后将arrayMethods继承自数组原型
// 这里是面向切片编程思想(AOP)--不破坏封装的前提下,动态的扩展功能
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]
methodsToPatch.forEach(function (method) { // 重写原型方法
const original = arrayProto[method] // 调用原数组的方法
def(arrayMethods, method, function mutator (...args) {
// 这里保留原型方法的执行结果
const result = original.apply(this, args)
// this代表的就是数据本身 比如数据是{a:[1,2,3]} 那么我们使用a.push(4) this就是a ob就是a.__ob__ 这个属性就是上 段代码增加的 代表的是该数据已经被响应式观察过了指向Observer实例
const ob = this.__ob__
// 这里的标志就是代表数组有新增操作
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 如果有新增的元素 inserted是一个数组 调用Observer实例的observeArray对数组每一项进行观测
if (inserted) ob.observeArray(inserted)
ob.dep.notify() // 当调用数组方法后,手动通知视图更新
return result
})
})
this.observeArray(value) // 进行深度监控