1.基元值
ref()和reactive()处理不同的基元值:字符串、数字、布尔值、null和undefined。
1.1 ref ()
refs()存储基元值和对象:
import { ref } from 'vue'
const numberRef = ref(0); // OK
const objectRef = ref({count: 0}); // OK
在上面的例子中,ref(0)创建了一个存储基元值的ref。
同样,ref({count: 0})创建了一个存储纯JavaScript对象的ref。
1.2 reactive()
另一方面,reactive()不存储基元,而只存储对象:
import { reactive } from 'vue'
const numberReactive = reactive(0); // not OK!
const objectReactive = reactive({count: 0}); // OK
使用基元值调用reactive(0)是不正确的。如果您需要生成反应基元值,那么ref(0)就是一种方法。
reactive()只适用于对象的原因是Vue的reactive实现。Vue使用代理截取对象的属性更改。代理不适用于基元。
尽管如此,用对象初始化的reactive({count:0)}是完全有效的,并创建了一个reactive对象。
2.访问无功数据
第二个区别是如何访问存储在ref()和reactive()中的数据。
2.1 ref ()
ref()数据,无论是基元值还是对象,都是通过一个特殊属性访问的。
import { ref } from 'vue';
const numberRef = ref(0);
console.log(numberRef.value); // 0
const objectRef = ref({count: 0});
console.log(objectRef.value.count); // 0
numberRef.value是从引用numberRef访问基元值的方式。
.value是所有ref上可用的特殊属性,用于读取或更新ref值。
此外,objectRef.value.count是如何访问引用中对象的属性的。
请注意,在模板内部,您不必使用.value来访问ref值:它们是自动展开的。
<script setup>
import { ref } from 'vue';
const numberRef = ref(0);
</script>
<template>
<div>{{ numberRef }}</div>
</template>
在插值 {{numberRef}} 中,ref是自动展开的。
2.2 reactive()
另一方面,reactive()数据是直接访问的:
import { reactive } from 'vue';
const objectReactive = reactive({count: 0});
console.log(objectReactive.count); // 0
访问使用reactive({count:0})创建的reactive数据不需要额外的语法,而是直接完成的:objectReactive.count。
reactive(originalObject)返回的reactive对象是originalObject的代理对象。这意味着反应对象具有与原始对象相同的属性(也就是具有相同的接口)。
3.重新分配数据
ref()是使用.value属性访问和更新的,而reactive()是原始对象的代理。因此,ref()可以重新分配给一个新对象,而reactive()不能。
3.1 ref()
将ref()的值完全重新分配为新值是完全有效的:
<script setup>
import { ref, onMounted } from 'vue';
const objectRef = ref({count: 0});
onMounted(() => { objectRef.value = {count: 1} })
</script>
<template>
<div>{{ objectRef.value }}</div>
</template>
在挂载后完全替换ref值objectRef.value={count:1}反映在输出中。保持反应性。
3.2 reactive()
但是,完全重新分配reactive()对象是不可能的:
<script setup>
import { reactive, onMounted } from 'vue';
const objectReactive = reactive({count: 0});
onMounted(() => { objectReactive.value = {count: 1} })
</script>
<template>
<div>{{ objectReactive.count }}</div>
</template>
安装后完全替换反应对象值objectReactive={count:1}并没有反映在输出中。这样做会破坏objectReactive的反应性。
4. 监视
watch()监视反应数据的变化。ref()和reactive()的watch()的默认行为不同。
4.1 ref()
watch()确定ref的.value属性是否已更改:
<script setup>
import { ref, watch } from 'vue';
const countNumberRef = ref(0);
watch(countNumberRef, () => {
console.log('changed!')
})
const increase = () => countNumberRef.value++
</script>
<template>
<div>{{ countNumberRef }}</div>
<button @click="increase">Increase</button>
</template>
每次单击“增加”按钮,您都会在控制台中看到消息“已更改!”。watch(count,callback)在每次countNumberRef.value更改时调用callback。
但是watch()是否监视存储在ref()中的对象的深层变化?
<script setup>
import { ref, watch } from 'vue';
const countObjectRef = ref({ count: 0 });
watch(countObjectRef, () => {
console.log('changed!')
})
const increase = () => countObjectRef.value.count++
</script>
<template>
<div>{{ countObjectRef.conut }}</div>
<button @click="increase">Increase</button>
</template>
然而,这一次,如果您单击“增加”按钮,控制台中将不会出现任何消息!结论是watch()默认情况下不会深入观察refs。
然而,当countObjectRef.value.count发生变化时,DOM仍然会更新:这意味着ref中的对象对于渲染的输出仍然是被动的。
当然,如果你让watch()深入观察裁判,它的工作原理是完全深入观察:
// ...
watch(count, () => {
console.log('changed!')
}, { deep: true })
// ...
4.2 reactive()
在监视反应对象的情况下,watch()始终执行深度监视(即使您没有指示{deep:true})选项。
<script setup>
import { reactive, watch } from 'vue';
const countObjectReactive = reactive({ count: { val: 0 } });
watch(countObjectReactive, () => {
console.log('changed!')
})
const increase = () => countObjectReactive.count.val++
</script>
<template>
<div>{{ countObjectReactive.conut.val }}</div>
<button @click="increase">Increase</button>
</template>
每次单击“增加”按钮,您都会在控制台中看到消息“已更改!”。每当countObjectReactive的任何属性(甚至是深层属性)发生变化时,watch(countObjectReactor,callback)都会调用callback。
5. 总结
这篇文章介绍了组合API中ref()和reactive()之间的差异:
1.ref()可以存储基元值,而reactive()不能。
2.您可以使用.value访问存储在ref()中的值,而reactive()对象可以直接用作常规对象。
3.ref()值可以重新分配给一个全新的对象,而reactive()不能。
4.watch()默认情况下只监视ref()的直接.value更改,同时对reactive()对象进行深度监视。