想要监听数据的变化就必须通过数据劫持,Proxy和defineProperty都能做数据劫持,但是数据劫持的方式不同。
核心都是对属性的读取和修改都要变成函数
vue2中defineProperty的做法
在vue源码中对对象进行了深度遍历对象中的每个属性做观察
const obj = {
name:'ohh',
friendA:{
name:'AAA',
age:18,
friends:{...}
}
function _isObject (v){
return typeof v === 'object' && v != null;
}
function observe(obj) {
for (const k in obj) {
let v = obj[k]
if(_isObject(v)){
observe(v);
}
Object.defineProperty(obj, k, {
get(){
console.log(k,'读取');
return k;
},
set(val){
if(val != v){
console.log(k,'更改')
v = val
}
}
})
}
}
// 观察者模式
observe(obj)
obj.name;
obj.name = "ahhh";
缺点:
- 1.性能差
- 2.如果对已经被观察的对象新增属性时,新的属性不会被观察(所以vue2中需要使用$set新增属性)
vue3中的Proxy做法
直接将对象变成一个新的代理对象,通过代理对象去做数据劫持
const obj = {
name: "ohh",
friendA: {
name: "AAA",
age: 18,
},
};
function _isObject(v) {
return typeof v === "object" && v != null;
}
function observe(obj) {
const proxy = new Proxy(obj, {
get(target, k) {
let v = target[k];
if (_isObject(v)) {
v = observe(v);
}
console.log(k, "读取", v);
return v;
},
set(target, k, val) {
if (target[k] != val) {
console.log(k, "更改");
target[k] = val;
}
},
});
return proxy;
}
const nObj = observe(obj);
nObj.name;
nObj.friendA.name;
nObj.friendA.name = "ppp";
nObj.name = "ahhh";
nObj.aaaaaaaa; //不存在的属性也会被劫持
优点: 不存在的属性也会被做劫持