详解Vue 如何监听Array的变化

2020-06-14 05:57:45易采站长站整理

// 获取原生的方法
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.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, )