记录一次修改Vue源码
大家都知道,在Vue中直接更改数组索引以更新数据是不奏效的。但请注意的是,在面对日益复杂的项目时,你的$set方法将会急剧增加。这将显著影响代码的可读性和维护效率。
在本次分享中, 我们将深入探讨如何对Vue源码进行调整, 使得触发更新事件时能够支持下标修改。
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value); // 看这里,那么Observer又是什么呢?
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
}
AI写代码
// 我们继续看
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
// 我们看到如果是数组,则执行this.observeArray,那么这个方法又做了什么呢?
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
// 看完上面的observeArray,我们继续看this.walk又做了什么?
this.walk(value);
}
};
// 原来这个observeArray,其实就是个递归,如果是数组就遍历,然后继续执行observe -> new Observer
Observer.prototype.observeArray = function observeArray (items) {
for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]);
}
};
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
// 我们继续看defineReactive$$1,又做了什么?
// 原来他的庐山真面目就是defineProperty,这下我们都明白了吧
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
...
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
...
},
set: function reactiveSetter (newVal) {
...
dep.notify();
}
});
}
AI写代码
曾我们在Vue源码中仅实现了对Object各个键值的监听功能,并未实现数组下标的监听机制。那我们该如何进行相应的修改呢?
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
// 我们看到如果是数组,则执行this.observeArray,那么这个方法又做了什么呢?
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
this.walk(value); // 我们只需要在这里添加这行,就可完成对下标的监听
} else {
// 看完上面的observeArray,我们继续看this.walk又做了什么?
this.walk(value);
}
};
AI写代码
完成以上修改后,我们再直接修改数组下标,页面也会进行更新
以上内容是我个人在项目开发中的一些体会(不主张大量使用$set),那么作为一个新手开发者来说,请问您是否也有类似的经历呢?
下面是我的回答链接:https://github.com/vuejs/vue/issues/8562

注意到这一点的人可能会质疑:性能的问题会不会因此变得难以应对?毕竟我们也可以考虑将数组视为一种特殊的对象结构——其中每个元素的位置由其键值唯一标识。然而这种结构在嵌套层次更加复杂的情况下可能会导致资源消耗过大——如果是嵌套更深的对象数组甚至说层级嵌套更加复杂的数据那么仅仅试图监听每一个元素的位置可能就会变得力不从心。为了验证这一现象是否存在显著影响在我的测试中使用了100次循环来观察不同情况下的表现结果表明在这种情况下面临的问题会使得渲染速度降低至少四倍以上这显然超出了可接受的范围尤其是对于那些对性能要求较高的项目来说这种做法可能会带来明显的性能瓶颈。
经过测试发现在这种情况下渲染时间会比不监听的情况慢出至少四倍因此可以说这种方法虽然可行但也值得谨慎对待特别是在项目规模较大或者涉及业务逻辑较为复杂的场景中建议避免这种行为以确保系统的稳定性和响应效率。
因此在大多数情况下这一特性不仅可行而且能提升代码的简洁性和可读性但这也取决于具体的应用场景和项目需求。
