我们都知道在vue中直接修改数组下标来修改数据,是不能触发vue的更新机制的,但是当你发现随着项目代码越来越多,你的$set会越来越多,这样会让你的代码可读性、维护性越来越差
修改vue源码,让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 }
// 我们继续看 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(); } }); }
原来在vue源码中,只对Object的各个key进行了监听,对数组下标并没有,那么我们如何修改呢?
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); } };
完成以上修改后,我们再直接修改数组下标,页面也会进行更新
以上内容是我自己平时写项目得到的想法(不想写那么多$set),那我这个菜鸡能想到,尤大就想不到吗?
下面贴上尤大对此处的回答https://github.com/vuejs/vue/issues/8562
看到了吧?因为性能!我们可以想数组也是对象,不过比较特殊,数组的下标是对象的key,那么如果是一个对象数组,甚至说层级嵌套更加深的数据,是不是监听每一个下标的话,会非常庞大,我使用了100个循环来验证,结果是如果监听数组下标的话渲染时间要比不监听的时候慢了4倍还不止!
所以,这个监听数组下标是可以实现,也可以让你的代码看起来更加优雅,但是如果项目特别小,或者只是自己的测试demo的话,可以这么玩,如果是公司项目的话,个人建议还是保持原样,因为涉及到性能!