浅谈Vue3中的reactive跟ref的区别

373 阅读4分钟

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); // 0const 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()对象进行深度监视。