vue-监听数组变化
Vue.js观察数组变化主要通过以下7个方法(push、pop、shift、unshift、splice、sort、reverse)
怎么实现?
通过对data数据中数组的这7个方法进行重新包装(注意只是data数据中的数组)
为什么不直接对Array.prototype的原型方法进行重新包装?
因为不应该过多地去影响全局
代码实现
1 const patchArray = (function () { 2 const methodsToPatch = [ 3 'push', 4 'pop', 5 'shift', 6 'unshift', 7 'splice', 8 'reverse', 9 'sort' 10 ]; 11 12 //设置对象属性的工具方法 13 function def (obj, key, val) { 14 Object.defineProperty(obj, key, { 15 value: val, 16 enumerable: true, 17 writable: true, 18 configurable: true 19 }); 20 } 21 22 const arrayProto = Array.prototype, //缓存Array的原型 23 arrayMethods = Object.create(arrayProto); //继承Array的原型 24 25 methodsToPatch.forEach(function (method, index) { 26 def(arrayMethods, method, function (...args) { 27 //首先调用Array原型的方法 28 const res = arrayProto[method].apply(this, args); 29 //data中每个数组都有一个__ob__的私有属性指向创建的Observer实例(有兴趣看看源码中的observe方法,这里不详述) 30 const ob = this.__ob__; 31 32 let inserted = null; 33 34 //记录插入的值 35 switch(method) { 36 case 'push': 37 case 'unshift': 38 inserted = args; 39 break; 40 case 'splice': 41 inserted = args.slice(2); 42 break; 43 } 44 45 if (inserted) { 46 //如果是调用了push、unshift、splice,则尝试对新插入的值进行响应式绑定,因为插入的值有可能是对象(Object)或者数组(Array) 47 ob && ob.observeArray(inserted); 48 } 49 50 console.log('数组发生改变了'); 51 52 //向所有依赖发送通知,告诉它们数组的值发生变化了 53 ob && ob.dep.notify(); 54 return res; 55 }); 56 }); 57 58 return function (target) { 59 //看看浏览器支不支持__proto__这个属性,通过改变__proto__的值,可以设置对象的原型 60 if ('__proto__' in {}) { 61 //将数组的原型指向arrayMethods,这样当数组调用上述的7个方法时,其实是调用arrayMethods中的方法而不是调用Array.prototype中的方法 62 target.__proto__ = arrayMethods; 63 } else { 64 //如果浏览器不支持__proto__,则设置数组对应的属性,这样当数组调用上述的7个方法时,其实是调用数组对应属性指向的方法 65 for (let i = 0, l = methodsToPatch.length; i < l; i++) { 66 let key = methodsToPatch[i]; 67 def(target, key, arrayMethods[key]); 68 } 69 } 70 } 71 })(); 72 73 //测试 74 let arr = [1, 2, 3]; 75 patchArray(arr); 76 arr.push(4);