详解Vue 如何监听Array的变化(2)

const arrayProto = Array.prototype // 获取Array的原型 function def (obj, key) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, value: function(...args) { console.log(key); // 控制台输出 push console.log(args); // 控制台输出 [Array(2), 7, "hello!"] // 获取原生的方法 let original = arrayProto[key]; // 将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变 const result = original.apply(this, args); // do something 比如通知Vue视图进行更新 console.log('我的数据被改变了,视图该更新啦'); this.text = 'hello Vue'; return result; } }); } // 新的原型 let obj = { push() {} } // 重写赋值 def(obj, 'push'); let arr = [0]; // 原型的指向重写 arr.__proto__ = obj; // 执行push arr.push([1, 2], 7, 'hello!'); console.log(arr);

被改变后的arr。

详解Vue 如何监听Array的变化

Vue源码解析

array.js

Vue在array.js中重写了methodsToPatch中七个方法,并将重写后的原型暴露出去。

// Object.defineProperty的封装 import { def } from '../util/index' // 获得原型上的方法 const arrayProto = Array.prototype // Vue拦截的方法 const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]; // 将上面的方法重写 methodsToPatch.forEach(function (method) { def(arrayMethods, method, function mutator (...args) { console.log('method', method); // 获取方法 console.log('args', args); // 获取参数 // ...功能如上述,监听到某个方法执行后,做一些对应的操作 // 1、将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变 // 2、视图更新等 }) }) export const arrayMethods = Object.create(arrayProto);

observer

在进行数据observer绑定的时候,我们先判断是否hasProto,如果存在__proto__,就直接将value 的 __proto__指向重写过后的原型。如果不能使用 __proto__,貌似有些浏览器厂商没有实现。那就直接循环 arrayMethods把它身上的这些方法直接装到 value 身上好了。毕竟调用某个方法是先去自身查找,当自身找不到这关方法的时候,才去原型上查找。

// 判断是否有__proto__,因为部分浏览器是没有__proto__ const hasProto = '__proto__' in {} // 重写后的原型 import { arrayMethods } from './array' // 方法名 const arrayKeys = Object.getOwnPropertyNames(arrayMethods); // 数组的处理 export function observeArray (value) { // 如果有__proto__,直接覆盖 if (hasProto) { protoAugment(value, arrayMethods); } else { // 没有__proto__就把方法加到属性自身上 copyAugment(value, arrayMethods, ) } } // 原型的赋值 function protoAugment (target, src) { target.__proto__ = src; } // 复制 function copyAugment (target, src, keys) { for (let i = 0, l = keys.length; i < l; i++) { const key = keys[i] def(target, key, src[key]); } }

通过上面的代码我们发现,没有直接修改 Array.prototype,而是直接把 arrayMenthods 赋值给 value 的 __proto__ 。因为这样不会污染全局的Array, arrayMenthods 只对 data中的Array 生效。

总结

因为监听的数组带来的代价和一些问题,Vue使用了重写原型的方案代替。拦截了数组的一些方法,在这个过程中再去做通知变化等操作。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/bc44238d4de9ca8955b98ac35715e550.html