vue2
对象内部的defineReactive方法,使用Object.defineProperty将属性进行劫持,重写getter和setter
const obj = {
name:'lihk',
age:25,
n:{
num:2222
}
}
function observer(data){
if(typeof data !=='object'||typeof data ===null){
return data
}
for (const key in data) {
defineReactive(data,key,data[key])
}
}
function defineReactive(target,key,value){
observer(value)//递归深度将其属性进行数据劫持
Object.defineProperty(target,key,{
// 重写get和set
get(){
return value
},
set(newValue){
if(value!==newValue){
value=newValue
// 将新值继续监听
observer(newValue)
}
}
})
}
observer(obj)
obj.c=111
console.log('obj', obj)
控制台打印其属性
缺点
重写getter和setter,性能差
新增和删除属性时无法监控变化,需要$set、$delete来更新属性进行重新监控
对ES6的Map、set这些数据结构不支持劫持
数组不采用defineProperty,对所有的索引进行劫持会造成性能浪费,则是通过重写数组的七个方法来实现的 ('push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice')
const obj = {
name: 'lihk',
age: 25,
n: {
num: 2222
},
m: [1, 2, 3]
};
//创建一个不影响原数组的对象来创建__proto__
let newArr = Object.create(Array.prototype);
let oldArr = Array.prototype;
['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'].forEach(
method => {
newArr[method] = function (...args) {
console.log('调用了', method);
// 将七个方法依次追加到数组里面,实现重写
oldArr[method].call(this, ...args);
};
}
);
function observer(data) {
if (typeof data !== 'object' || typeof data === null) {
return data;
}
if (Array.isArray(data)) {
// 判断是不是数组
data.__proto__ = newArr;
} else {
// 是对象属性
for (const key in data) {
defineReactive(data, key, data[key]);
}
}
}
function defineReactive(target, key, value) {
observer(value); //递归深度将其属性进行数据劫持
Object.defineProperty(target, key, {
// 重写get和set
get() {
return value;
},
set(newValue) {
if (value !== newValue) {
value = newValue;
// 将新值继续监听
observer(newValue);
}
}
});
}
observer(obj);
obj.m.push(2, 3, 4);
console.log('obj', obj.m);
将七个方法追加到数组的prototype上面
缺点
数组的索引和长度变化无法检测到
vue3
使用proxy进行getter和setter代理,递归是惰性的,只有触发get才会将其变成proxy,vue2需要全部添加get和set
const obj = { name: 'lihk', age: 25, n: [1, 2, 3, 4] }
function reactive(target) {
return new Proxy(target, handler)
}
let handler = {
get(target, key) {
// 收集effect
let temp = target[key]
if (typeof temp === 'object') {
// 递归是惰性的,只有触发get才会将其变成proxy,vue2需要全部添加get和set
return new Proxy(temp, handler)
}
return temp
},
set(target, key, value) {
// 触发effect
target[key] = value
console.log('key', key)
},
}
const proxy = reactive(obj)
console.log('proxy', proxy,proxy.n)