前言
在讲setup
的文章中我们已经看到了点击按钮后函数执行了但却不会引起视图更新,这是因为它们的数据不是响应式的数据,而这就不得不提到本文的主角ref
和reactive
了,这两者虽然都可以用于实现响应式数据绑定,但在使用场景、语法以及适用的数据类型上有所不同。本文将详细介绍这两种方法,并讨论如何通过toRefs
和toRef
进一步增强响应式的灵活性。
ref
:基本类型和对象类型的响应式数据
ref
的主要作用是定义一个响应式变量,无论是基本类型还是对象类型的数据都可以用它来包裹。当使用ref
创建响应式数据时,返回的是一个RefImpl
实例对象,这个对象的value
属性是响应式的。
基本类型的响应式数据
- 语法:let xxx = ref(初始值)
- 返回值:一个
RefImpl
的实例对象 - 注意点:
- 在js中操作
ref
对象的数据时需要时刻注意.value
,但模板中不需要.value,直接使用即可。
- 在js中操作
代码示例:
<template>
<div class="person">
<h1>姓名:{{name}}</h1>
<h1>年龄:{{age}}</h1>
<button @click="changeName">修改姓名</button>
<button @click="changeAge">点击年龄+1</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue' // 引入ref
let name = ref('张三')
let age = ref(18)
function changeName(){
name.value = '李四' // 这里一定要加value,没加会报错
console.log(name) // 这里不加value,打印不出来
}
function changeAge(){
age.value += 1
console.log(age.value) // 这里加value,能打印出来
}
// 返回一个对象,对象中的属性和方法会被暴露给模板
</script>
<style scoped>
</style>
可以看到,由于上面代码中console.log(name)
没有加上.value
,所以它的值在控制台上是一个RefImpl
的实例对象,而age
就能正确打印出来。
对象类型的响应式数据
尽管ref
主要用于基本类型,但它同样可以处理对象类型的数据。在这种情况下,ref
实际上是在内部调用了reactive
函数,使得对象成为响应式的。
代码示例:
<template>
<div class="person">
<h1>手机:一部{{ phone.brand }}手机,价格为{{ phone.price }}元</h1>
<h1>书籍列表:</h1>
<ul>
<li v-for="b in books" :key="b.id">{{ b.name }}</li>
</ul>
<h2>测试:{{obj.a.b.c.d}}</h2>
<button @click="changeCarPrice">修改手机价格</button>
<button @click="changeFirstGame">修改第一本书的名字</button>
<button @click="test">测试</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { ref } from 'vue'
let phone = ref({ brand: '华为', price: 6000 })
let books = ref([
{ id: '01', name: '西游记' },
{ id: '02', name: '红楼梦' },
{ id: '03', name: '三国演义' }
])
let obj = ref({
a:{
b:{
c:{
d:666
}
}
}
})
console.log(phone)
function changeCarPrice() {
phone.value.price += 100
}
function changeFirstGame() {
books.value[0].name = '水浒传'
}
function test(){
obj.value.a.b.c.d = 999
}
</script>
reactive
:对象类型的响应式数据
reactive
专门用来创建对象类型的响应式数据。它接收一个普通对象作为参数,返回一个深层的响应式代理对象,这意味着嵌套的对象也是响应式的。
- 语法:let 响应式对象 = reactive(源对象)
- 返回值: 一个
Proxy
的实例对象
代码示例:
<template>
<div class="person">
<h1>手机:一部{{ phone.brand }}手机,价格为{{ phone.price }}元</h1>
<h1>书籍列表:</h1>
<ul>
<li v-for="b in books" :key="b.id">{{ b.name }}</li>
</ul>
<h2>测试:{{obj.a.b.c.d}}</h2>
<button @click="changeCarPrice">修改手机价格</button>
<button @click="changeFirstGame">修改第一本书的名字</button>
<button @click="test">测试</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { reactive } from 'vue'
let phone = reactive({ brand: '华为', price: 6000 })
let books = reactive([
{ id: '01', name: '西游记' },
{ id: '02', name: '红楼梦' },
{ id: '03', name: '三国演义' }
])
let obj = reactive({
a:{
b:{
c:{
d:666
}
}
}
})
function changeCarPrice() {
phone.price += 100
}
function changeFirstGame() {
books[0].name = '水浒传'
}
function test(){
obj.a.b.c.d = 999
}
</script>
可以看到,这同样也实现了响应式数据,最后值得注意的是,直接重新赋值给reactive
对象会导致失去响应性,因此如果要替换整个对象,建议使用Object.assign
或其他方式整体更新。
ref
vs reactive
ref
用来定义基本类型数据和对象类型数据,reactive
只能用来定义对象类型数据
-
区别
ref
创建的变量必须使用.value
reactive
会重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)
-
使用方法:
- 基本类型:必须使用
ref
。 - 浅层对象:
ref
和reactive
皆可选用。 - 深层对象:推荐使用
reactive
以确保所有层级的响应性。
- 基本类型:必须使用
增强响应式数据的工具:toRefs
和 toRef
为了更方便地解构响应式对象同时保持其响应性,Vue提供了toRefs
和toRef
两个辅助函数。
toRefs
:将响应式对象的所有属性转换为独立的ref
对象,适合批量转换。toRef
:用于从响应式对象中提取单个属性并转换为ref
对象。
小结
综上所述,在Vue 3中,ref
和reactive
为我们提供了灵活且强大的手段来管理应用中的响应式数据。根据具体的需求场景合理选择合适的工具,能够帮助我们构建更加高效、易于维护的前端应用。此外,借助toRefs
和toRef
,我们可以更加便捷地操作响应式数据,确保应用的状态管理既清晰又强大。