vue中的变化检测问题

2020-03-17  本文已影响0人  frank_松

从vue通过Object.defineProperty方法劫持数据可知,vue是监听不到对象属性的增加或删除的,因为这些属性没有被劫持。想要监听数据变化,我们就要将其转换为响应式,这时候可以用this.$set(data, key, value)。

这个$set里面做了什么?

一、 给一个对象新增属性
我们将源码逻辑简化:

set(target, key, val) {
    ...
    const ob = target._ob_; // 获取对象的_ob_属性,它为劫持数据时创建的observer实例
    ...
    defineReactive(obj, key, val); // 通过definedReactive方法劫持属性,将其转换为响应式
    ob.dep.notify(); // 手动让发布者发送消息,通知订阅者进行更新
    return val;
}

二、给一个数组的一个位置添加元素

set(target, key, val) {
    ...
    if (Array.isArray(target)) { // 判断是数组
        target.length = Math.max(target.length, key); // 更新数组长度
        target.splice(key, 1, val); // 通过splice方法向数组指定位置插入元素
        return val;
    }
    ...
}

或许有人会问了,splice方法会改变原始数组,但是不改变数组在堆内存中的地址,为毛可以通过splice插入元素的方式触发订阅者更新?其实vue内部将数组原型中的方法篡改了,主要做了什么呢?

我们先列举出会改变原始数组的原型方法

const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

源码中依次遍历这些原型方法,改写其逻辑。主要是:首先获取原型方法执行后的返回值,然后通过数组的“ob”属性发出消息通知订阅者更新,然后将值返回。当使用splice,push,unshift这三个方法时,会额外对新增的元素做一次劫持。

由于原型方法已经被篡改了,只要使用这些方法修改数组,就会触发响应。

注:
1、直接修改数组某个索引的元素触发响应,arr[index] = newVal;
2、修改数组的长度length不会触发更新, arr.length = newVal;

相比大家也明白的原因,不在阐述。有问题请指出,谢谢!

上一篇 下一篇

猜你喜欢

热点阅读