响应式数据管理:ref与reactive

381 阅读4分钟

前言

在讲setup的文章中我们已经看到了点击按钮后函数执行了但却不会引起视图更新,这是因为它们的数据不是响应式的数据,而这就不得不提到本文的主角refreactive了,这两者虽然都可以用于实现响应式数据绑定,但在使用场景、语法以及适用的数据类型上有所不同。本文将详细介绍这两种方法,并讨论如何通过toRefstoRef进一步增强响应式的灵活性。

ref :基本类型和对象类型的响应式数据

ref的主要作用是定义一个响应式变量,无论是基本类型还是对象类型的数据都可以用它来包裹。当使用ref创建响应式数据时,返回的是一个RefImpl实例对象,这个对象的value属性是响应式的。

基本类型的响应式数据

  • 语法:let xxx = ref(初始值)
  • 返回值:一个RefImpl的实例对象
  • 注意点:
    • 在js中操作ref对象的数据时需要时刻注意.value,但模板中不需要.value,直接使用即可。

代码示例:

<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>
ref.gif

可以看到,由于上面代码中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>
ref.gif

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>
ref.gif

可以看到,这同样也实现了响应式数据,最后值得注意的是,直接重新赋值给reactive对象会导致失去响应性,因此如果要替换整个对象,建议使用Object.assign或其他方式整体更新。

ref vs reactive

ref用来定义基本类型数据和对象类型数据,reactive只能用来定义对象类型数据

  • 区别

    • ref创建的变量必须使用.value
    • reactive会重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)
  • 使用方法:

    • 基本类型:必须使用ref
    • 浅层对象refreactive皆可选用。
    • 深层对象:推荐使用reactive以确保所有层级的响应性。

增强响应式数据的工具:toRefstoRef

为了更方便地解构响应式对象同时保持其响应性,Vue提供了toRefstoRef两个辅助函数。

  • toRefs:将响应式对象的所有属性转换为独立的ref对象,适合批量转换。
  • toRef:用于从响应式对象中提取单个属性并转换为ref对象。

小结

综上所述,在Vue 3中,refreactive为我们提供了灵活且强大的手段来管理应用中的响应式数据。根据具体的需求场景合理选择合适的工具,能够帮助我们构建更加高效、易于维护的前端应用。此外,借助toRefstoRef,我们可以更加便捷地操作响应式数据,确保应用的状态管理既清晰又强大。