ref vs reactive

refreactive 都是用于在 Vue 的组合式 api 中定义一个响应式变量,但他们的用法又略有区别。

ref

ref 可以用于任何类型,但对其操作时必须使用 valueref 返回的是一个经过 proxy 包装后的响应式对象。 $$tip

响应式的原理是使用到了 JavaScript 的 proxy 来实现的,因为 proxy 是创建的对象的代理,因而必须使用对象才能达到响应式的效果。具体参考 Vue 的官方文档深入响应式系统

$$

<template>
    <!-- 错误 -->
    <!-- <h1>{{ counter.value }}</h1> -->

    <!-- 正确 -->
    <h1>{{ counter }}</h1>
    <button @click="count">加一</button>
    <button @click="setZero">归零</button>
</template>
<script setup>
import { ref } from "vue"

let counter = ref(0)

function count() {
    // 错误
    // counter++

    // 正确
    counter.value++
}

function setZero() {
    // 错误
    // counter = 0

    // 正确
    counter.value = 0
}
</script>

对于对象类型, ref 也可以很好地胜任。

<template>
    <h1>{{ counter.n }}</h1>
    <button @click="count">加一</button>
    <button @click="setZero">归零</button>
</template>
<script setup>
import { ref } from "vue"

let counter = ref({
    n: 0,
})

function count() {
    counter.value.n++
}

function setZero() {
    counter.value.n = 0
}
</script>

reactive

不过对于对象类型,更习惯使用 reactive ,这样可以省去写 value 的麻烦。

$$warning

reactive 只能用于对象类型,不能用于原始类型。

$$

<template>
    <h1>{{ counter.n }}</h1>
    <button @click="count">加一</button>
    <button @click="setZero">归零</button>
</template>
<script setup>
import { reactive } from "vue"

let counter = reactive({
    n: 0,
})

function count() {
    counter.n++
}

function setZero() {
    counter.n = 0
}
</script>

重新赋值

当我们对一个 reactive 变量重新赋值时,会发现其不起作用,不能触发双向响应。

<template>
    <h1>{{ mingren.name }}</h1>
    <button @click="changeMingren">修改鸣人</button>
    <h1>{{ ningci.name }}</h1>
    <button @click="changeNingci">修改宁次</button>
</template>
<script setup>
import { ref, reactive } from "vue"

let mingren = ref({ name: "鸣人" })
let ningci = reactive({ name: "宁次" })

function changeMingren() {
    mingren.value = { name: "鸣人(修改)" } // OK
}

function changeNingci() {
    ningci = { name: "宁次(修改)" } // 不起作用
}
</script>

根本原因在于, 双向响应是通过proxy 监听属性更改实现的,而 reactive 变量重新赋值是替换了整个变量,而不是修改变量中的属性。

针对需要重新赋值整个变量的情况,可以使用 ref 或者 Object.assign。因为 Object.assign 是把一个对象的所有属性赋值给另一个对象。

<template>
    <h1>{{ mingren.name }}</h1>
    <button @click="changeMingren">修改鸣人</button>
    <h1>{{ ningci.name }}</h1>
    <button @click="changeNingci">修改宁次</button>
</template>
<script setup>
import { ref, reactive } from "vue"

let mingren = ref({ name: "鸣人" })
let ningci = reactive({ name: "宁次" })

function changeMingren() {
    mingren.value = { name: "鸣人(修改)" } // OK
}

function changeNingci() {
    // ningci = { name: "宁次(修改)" } // 不起作用

    Object.assign(ningci, { name: "宁次(修改)" }) // ok
}
</script>