# reactive
reactive
会对传入的引用类型进行包裹,创建一个该对象的 Proxy
代理,它是源对象的响应式副本 —— copy
,不等于原始对象
const raw = {};
const proxy = reactive(raw);
// 代理和原始对象不是全等的
console.log(proxy === raw); // false
reactive
定义引用数据类型(以对象和数组举例),它能够将复杂数据类型的内部属性或者数据项声明为响应式数据,所以reactive
的响应式是深层次的,其底层是通过ES6
的Proxy
来实现数据响应式
import { reactive } from "vue";
const proxy = reactive({});
const raw = {};
proxy.nested = raw;
console.log(proxy.nested === raw); // false
console.log(proxy.nested === reactive(raw)); // true
在给 reactive 封装的对象设置内部属性时,如果这个属性是引用数据类型,那这个属性也会被封装成 Proxy 对象 ——
所以相对于Vue2
的Object.defineProperty
,具有能监听增删操作,能监听对象属性的变化等优点
# 基本类型参数
基本数据类型(数字、字符串、布尔值)在reactive
中是无法被创建成proxy
对象的,也就无法实现监听
<template>
<div>
<p>count: {{ count }}</p>
<button @click="c">点击增加count</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let count = reactive(0);
const c = function () {
count++;
console.log("count: ", count);
};
</script>
点击button
,发现界面上的count
没有发生变化
出现提示
value cannot be made reactive: 0
但是我们打印count
,发现count
的值发生了改变,但没有体现到页面上,这就是没有实现响应式,因为基本类型无法被创建成proxy
对象,此刻count
的类型是Number
这就是ref的优势了,当然,如果你非要通过reactive
来设置数据,那就需要把基本数据类型转为对象类型来存储 👇
<template>
<div>
<p>count: {{ count.val }}</p>
<button @click="c">点击增加count</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let count = reactive({
val: 0,
});
const c = function () {
count.val++;
console.log("count: ", count.val);
};
console.log(count);
</script>
这样就可以实现响应式数据的需求了,但需要注意的是,这样配置对象的方式,count.val
的类型仍然是Number
,不是proxy
代理的对象
作为对比,可以参考下面的代码
<template>
<div>
<p>count.val: {{ count.val }}</p>
<p>count.out.inner: {{ count.out.inner }}</p>
<button @click="c">点击增加count</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let count = reactive({
val: 0,
out: {
inner: 0,
},
});
const c = function () {
count.val++;
count.out.inner++;
console.log("count.val: ", count.val);
console.log("count.out.inner: ", count.out.inner);
};
console.log(count);
console.log(count.out);
</script>
上面这个例子这也证明了reactive
的响应式是深层次的 —— 生成只有引用数据类型被封装成了Proxy
对象,基本类型仍然是基本类型,但是由于外层包裹了一层Proxy
,所以响应式是实现了的
# 不常见的数据类型
给reactive
传入一个Date
对象
<template>
<div>
<p>{{ date }}</p>
<button @click="c">点击增加时间</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let date = reactive(new Date());
const c = function () {
console.log("pre_date: ", date);
date.setDate(date.getDate() + 1);
console.log("cur_date: ", date);
};
</script>
点击button
,又出现了之前的问题,数据发生了改变,但是没有响应式到页面上,我们通过之前的方法,在date
外层加一层对象,发现仍然是不响应的
<template>
<div>
<p>{{ date.val }}</p>
<button @click="c">点击增加时间</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let date = reactive({
val: new Date(),
});
const c = function () {
console.log("pre_date: ", date.val);
date.val.setDate(date.val.getDate() + 1);
console.log("cur_date: ", date.val);
};
</script>
那这种情况,就要使用另一种方法来实现响应式了 —— 重新赋值
<template>
<div>
<p>{{ date.val }}</p>
<button @click="c">点击增加时间</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let date = reactive({
val: new Date(),
});
const c = function () {
console.log("pre_date: ", date.val);
date.val.setDate(date.val.getDate() + 1);
date.val = new Date(date.val);
console.log("cur_date: ", date.val);
};
</script>
把修改后的date.val
放到一个新的Date
对象里,然后重新赋值给date.val
,这样就可以实现响应式的效果了 —— 相当于浅拷贝
# 总结
reactive
是Vue3
中提供的实现响应式数据的方法。- 在
Vue2
中响应式数据是通过Object.defineProperty
来实现的, - 在
Vue3
中响应式数据是通过ES6
的Proxy
来实现的。 reactive
参数必须是对象 (json
/arr
)等- 如果给
reactive
传递了其它不常用的对象,默认情况下,修改对象无法实现界面的数据绑定更新。如果需要更新,需要进行重新赋值。(即不允许直接操作数据,需要放个新的数据来替代原数据)