之前写过一段vue2.x响应式缺陷,现在来写一写vue3.0中是否解决了2.x存在的问题。
- 新增属性、删除属性,页面不会更新。
- 直接通过下标修改数组,界面不会自动更新。
写一个模板,来试一试vue3.0新增属性并且打印
<template>
<p>个人信息</p>
<p>姓名:{{ person.name }}</p>
<p>年龄:{{ person.age }}</p>
<p>妈妈:{{ person.family.mother }}</p>
<p>爸爸:{{ person.family.father }}</p>
<p>兄弟:{{ person.family.brother ? person.family.brother : "还没有兄弟" }}</p>
<p>爱好:{{ person.hobby }}</p>
<button @click="add">新增家庭成员</button>
<button @click="edit">修改个人爱好</button>
<button @click="del">删除爱好</button>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from "vue";
interface Person<T> {
age: number;
name: string;
family: T;
hobby?: string[];
}
interface Family {
mother: string;
father: string;
brother?: string;
}
export default defineComponent({
name: "Home",
components: {},
setup() {
const person: Person<Family> = reactive({
name: "张三",
age: 18,
family: {
mother: "李四",
father: "王五",
},
hobby: ["足球", "篮球", "羽毛球"],
});
const add = () => {
person.family.brother = "赵六";
console.log("person add", person);
};
const edit = () => {
person.hobby![0] = "学习";
console.log("person edit", person);
};
const del = () => {
delete person.hobby;
console.log("person del", person);
};
return {
person,
add,
edit,
del,
};
},
});
</script>
通过打印发现,数据发生变化,页面同步更新,完美解决了vue2.x数据响应式的问题。    现在我们来看一下vue3.0中的响应式proxy 先写一个模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let person = {
name: "张三",
age: 18,
};
</script>
</body>
</html>
写一段vue2.x的简单数据响应式,方便对比
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let person = {
name: "张三",
age: 18
};
let a = {};
Object.defineProperty(a, "name", {
get() {
return person.name;
},
set(val) {
console.log("set name", val);
person.name = val;
},
});
Object.defineProperty(a, "age", {
get() {
return person.age;
},
set(val) {
console.log("set age", val);
person.age = val;
},
});
</script>
</body>
</html>
打印正常  修改a.name值为李四,触发setter,正常打印数据(vue复杂响应式逻辑,这里就不写了,简单模拟一下),模拟响应式。 然后正常添加一个数据(非响应式数据)  再删除一个数据,显示false,a上仍有属性name,(这是因为没有配置configurable)  配置完就可以正常删除,但是可以发现,不同于读和写,虽然可以删除/添加,但是无法捕获到删除的操作,从以上逻辑及打印可以看得出,vue2.x还是有点小缺陷的。  再来看一下vue3中响应式 首先了解一个window.Porxy,Porxy属于window上内置的一个构造函数  先简单的使用proxy,打印(查看)一下属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person,{});
</script>
</body>
</html>
 接着我们尝试一下增删改的操作,发现proxy都可以捕获到,做到了响应式的第一层:数据代理(修改了p的数据,person发生变化),但是未实现响应式的操作,就比如之前vue2.x打印的那个位置,实际上是数据响应式的逻辑,如删除xx,更新xx,只不过现在方便实现,就用console.log代替)  接着我们来捕获一下数据的修改,并打印
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person, {
get(a, b) {
console.log("a", a);
console.log("b", b);
return 1;
},
set() {},
});
</script>
 这里的a,指的是源对象person,也就是传入的对象,b是读取的属性,这里就可以发现,区别于Object.defineProperty()单独处理数据,Proxy可以批量处理数据,无论person中有多少属性,只需要一个Proxy就可以处理
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person, {
get(target, propName) {
console.log(`读取a中${target[propName]}属性`);
return target[propName];
},
set() {},
});
</script>
再来看一下setter的操作
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person, {
get(target, propName) {
console.log(`读取a中${target[propName]}属性`);
return target[propName];
},
set(target, propName, value) {
console.log("target", target);
console.log("propName", propName);
console.log("val", value);
},
});
</script>
 完整的Proxy读/写代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person, {
get(target, propName) {
console.log(`读取a中${target[propName]}属性`);
return target[propName];
},
set(target, propName, value) {
console.log(`写a中${target[propName]}属性,更新页面`);
target[propName] = value;
},
});
</script>
</body>
</html>
接着看一下区别于Object.defineProperty(),Porxy上有的删除的操作,打印如下
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person, {
get(target, propName) {
console.log(`读取a中${target[propName]}属性`);
return target[propName];
},
set(target, propName, value) {
console.log(`写a中${target[propName]}属性,更新页面`);
target[propName] = value;
},
deleteProperty(target, propName) {
console.log(`删a中${target[propName]}属性,更新页面`);
return delete target[propName];
},
});
</script>

最后,当我们从控制台增加属性时,打印如下,触发了写的操作,也即是说,set不仅在修改时会被调用,增加时也会被调用。  完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let person = {
name: "张三",
age: 18,
};
const a = new Proxy(person, {
get(target, propName) {
console.log(`读取a中${target[propName]}属性`);
return target[propName];
},
set(target, propName, value) {
console.log(`写a中${target[propName]}属性,更新页面`);
target[propName] = value;
},
deleteProperty(target, propName) {
console.log(`删a中${target[propName]}属性,更新页面`);
return delete target[propName];
},
});
</script>
</body>
</html>
|