vue2 vs vue3 系列文章
可浏览博客主页的Vue专栏,会陆续添加相关文章,有问题或者可以优化的地方也希望大大门告知
共同进步 :)
vue2.x 的响应式
实现原理
- 对象类型:通过Object.defineProperty() 对属性的读取跟修改进行拦截(数据劫持)。
- 数组类型:通过重写更新数据的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
let obj={name:"Penk666"}
Object.defineProperty(obj,'name',{
get(){},
set(value)(){}
})
存在问题
- 新增属性、删除属性,界面不会更新。
- 直接通过下标修改数组,界面不会更新。
错误测试代码
此处的错误就是没有像对obj进行判断是否为对象就开始遍历~ 傻逼操作了,引以为戒...
/**
* vue2 实现对象数据劫持
*/
var person = { name: 'Penk666', age: 32, job: { j1: { salary: 10000 } } };
function Observer(obj) {
// 获取对象的键名
let keys = Object.keys(obj);
// 遍历
for (let i of keys) {
// 如果是对象,并且不是null(null也是对象),则继续监听,形成递归
if (typeof obj[i] == 'object' && typeof obj[i] !== null) {
Observer(obj[i]);
}
// 主方法,监听对象的属性。
// 缺点:无法监听新增对象obj.xxx 或者删除对象 delete obj.a。
Object.defineProperty(obj, i, {
get() {
console.log(`数据被获取了${obj[i]}...`);
return obj[i];
},
set(newValue) {
console.log(`数据变化了,obj.${i}:${newValue}...`);
obj[i] = newValue;
},
enumerable: true,
configurable: true
});
}
}
Observer(person);
console.log('person:', person);
console.log('name:', person.name);
如下图:
解决方案:先判断obj是否为对象,再进行遍历。由于vue的data为obj,所以第一步就可以进行判断了~
约定俗成的东西记住即可~~~
测试代码
这边使用es6中的class,使用面向对象编程的写法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
/**
* vue2 实现对象数据劫持
*/
var person = { name: 'Penk666', age: 32, job: { salary: 10000 } };
// 数据劫持
class Observer {
constructor(data) {
//初始化时候劫持数据
this.observer(data);
}
observer(data) {
if (data && typeof data == 'object') {
for (let key in data) {
// 这里传入data[key] 是为了形成闭包,避免循环调用造成的内存泄漏
this.defineReactive(data, key, data[key]);
}
}
}
defineReactive(obj, key, val) {
this.observer(val);
Object.defineProperty(obj, key, {
enumerable: true,
get() {
console.log(`Object.defineProperty监听到数据被获取了,当前值${val}...`);
return val;
},
set: (newVal) => {
if (val == newVal) return;
console.log(`Object.defineProperty监听到数据修改了,当前值${newVal}...`);
val = newVal;
// 重新赋值的时候劫持数据
this.observer(newVal);
}
});
}
}
new Observer(person);
</script>
</body>
</html>
测试用例
温馨提示:不要使用++ 操作…
// get 获取属性值
person.name;
console.log('@@');
// set 设置属性值
person.name += '!';
console.log('@@');
// 深层次对象 set
person.job.salary=22;
console.log('@@');
效果如下图:
vue3.x 的响应式
优点:可以监听到对象的新增,以及删除操作;不需要递归实现。
实现原理
- 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读取、属性的添加、属性的删除等。
- 通过Reflect(反射):对被代理对象的属性进行操作。
虽然可以直接在obj上操作,但是JS已经开始将Object对象的方法转移到Reflect上了,怕是为了解耦。
测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
/**
* vue3 实现对象数据劫持
*/
var o = { a: 1, b: 2, c: { d: 3 } };
var arr = [1, 2, 3];
function Observer(obj) {
return (p = new Proxy(obj, {
get(target, key) {
console.log(`proxy对象监听到数据被获取了,当前值${target[key]}...`, target, key);
return Reflect.get(target, key);
},
set(target, key, newValue) {
console.log('proxy对象监听到数据新增或更新了...', target, key, newValue);
Reflect.set(target, key, newValue);
},
deleteProperty(target, key) {
console.log(`proxy对象监听到源对象删除${key}了...`);
Reflect.deleteProperty(target, key);
}
}));
}
var proxy = Observer(o);
</script>
</body>
</html>
测试用例
// get一个属性
proxy.a;
// 删除一个属性
delete proxy.b;
// 新增一个属性
proxy.e = 6;
// 修改深层属性
proxy.c.cc.ccc = 33;
效果如下图: