vue3.x教程-toRefs()、toRef()函数-解构 reactive 对象

提出问题

直接 解构 reactive 对象,变量就不是响应式

<template>
    姓名:{{name}}<br>
    年龄:{{age}}<br>
    性别:{{sex}}<br>
    身高:{{height}}<br>
    <button @click="addAge">年龄加1</button>
    <button @click="modifyName">修改名字</button>
</template>

<script lang="ts" setup name='Student'>
    import { reactive } from 'vue'

    let student = reactive({
        name: '李雷',
        age: 20,
        sex: '男',
        height: 1.82
    })
    // ❌ 直接解构,name、age等 不是响应式
    let { name, age, sex, height } = student
    // 通过打印可知就是普通的值
    console.log("name:",name)
    console.log("age:",age)
    console.log("sex:",sex)

    function addAge() {
        // 执行函数,页面的值不变化
        age++
    }
    function modifyName() {
        // 执行函数,页面的值不变化
        name = '李大雷'
    }
</script>

为什么要解构 reactive 对象

工程化项目里 解构是主流写法,在模板、函数中使用变量更简单,不用写 student.

注意

可能有的人看到这里会迷糊,既然需要 解构,那为什么还要定义 student 对象?为什么还要将 name、age 等属性定义在对象中?

因为下面属性是描述一个学生的:

name: '李雷',
age: 20,
sex: '男',
height: 1.82

按照面向对象的编程思想,就应该放到对象中

解构是为了在模板、函数中使用变量更简单,不用写 student.

解决

使用 toRefs、toRef 解构后仍保留响应式

toRefs() 函数

toRefs 接收一个 响应式对象(reactive 创建),把对象每一层属性都转为独立的 ref,返回一个普通对象,所有属性都是 ref。

规则

  1. 参数只能是 reactive / shallowReactive 响应式对象;
  2. 返回新对象,原对象不变;
  3. 每个属性都是 ref,取值需要 .value;
  4. 只转换自身可枚举属性,不处理深层对象;
  5. 不能用于原始值、ref、数组。

例子

<template>
    姓名:{{name}}<br>
    年龄:{{age}}<br>
    性别:{{sex}}<br>
    身高:{{height}}<br>
    <button @click="addAge">年龄加1</button>
    <button @click="modifyName">修改名字</button>
</template>

<script lang="ts" setup name='Student'>
    import { reactive,toRefs } from 'vue'

    let student = reactive({
        name: '李雷',
        age: 20,
        sex: '男',
        height: 1.82
    })

    let { name, age, sex, height } = toRefs(student)
    // 还是ref对象
    console.log("name:",name)
    console.log("age:",age)
    console.log("sex:",sex)

    function addAge() {
        // 通过 .value 改变值
        age.value++
    }
    function modifyName() {
        // 通过 .value 改变值
        name.value = '李大雷'
    }
</script>

<style>
</style>

解释

  • name、age 等变量 是 Ref 实例,模板自动解包,修改时同步更新原 student。

  • 模板里不用 .value,script 内部必须加 .value

toRef() 函数

提取对象某一个属性,生成关联原对象的 ref

语法

toRef(target, key, defaultValue?)

参数解释:

  • target:reactive 响应式对象
  • key:要提取的属性名,字符串类型
  • defaultValue?:当 target 上不存在 key 这个属性时,ref 默认取该值。不会自动同步到原对象。可略

例子

<template>
    姓名:{{name}}<br>
    年龄:{{age}}<br>
    性别:{{sex}}<br>
    身高:{{height}}<br>
    <button @click="addAge">年龄加1</button>
    <button @click="modifyName">修改名字</button>
</template>

<script lang="ts" setup name='Student'>
    import { reactive,toRef } from 'vue'

    let student = reactive({
        name: '李雷',
        age: 20,
        sex: '男',
        height: 1.82
    })

    let name = toRef(student,'name')
    let age = toRef(student,'age')
    let sex= toRef(student,'sex')
    let height = toRef(student,'height')
    // 还是ref对象
    console.log("name:",name)
    console.log("age:",age)
    console.log("sex:",sex)

    function addAge() {
        // 通过 .value 改变值
        age.value++
    }
    function modifyName() {
        // 通过 .value 改变值
        name.value = '李大雷'
    }
</script>

<style>
</style>

toRefs vs toRef 区别

  • toRefs(obj):批量把对象所有属性转 ref,返回对象,适合解构;

  • toRef(obj, key):单独转换对象某一个属性,只生成单个 ref。

注意:深层对象处理

toRefs 只处理第一层,嵌套对象内部不会自动转 ref:

const state = reactive({
  user: {
    username: 'test'
  }
})
const { user } = toRefs(state)
// user 是 ref,user.value 仍是 reactive 对象
console.log(user.value.username)
// 修改嵌套
user.value.username = 'admin'

如果需要深层全部转 ref,需递归处理或使用 toRefs 逐层解构。

常见坑 & 注意事项

坑1:传入普通对象,无响应

const obj = { name: 'xxx' }
const { name } = toRefs(obj) // 无效,不会响应

必须传入 reactive 代理对象。

坑2:数组不能用 toRefs

toRefs 设计用于对象,数组请用 toRef 单独取值或 ref([])。

const arr = reactive([1,2,3])
const refs = toRefs(arr) // 不推荐,行为异常

坑3:新增属性不会被 toRefs 捕获

toRefs 只会转换调用时已存在的属性,后续新增属性无法解构:

const state = reactive({ name: '张三' })
const refs = toRefs(state)
state.age = 18 // 后新增,refs 里没有 age

坑4:不要覆盖 ref 变量

const { name } = toRefs(state)
name = '新值' // 错误!直接覆盖ref对象,丢失响应
name.value = '新值' // 正确

总结

  1. toRefs(reactiveObj) 将响应式对象所有属性批量转为 ref;
  2. 解决解构 reactive 丢失响应式问题;
  3. 模板直接使用,script 操作加 .value;
  4. 组合式函数抽离逻辑时标准返回方案;
  5. 仅处理第一层对象,不递归深层;只接受 reactive 对象。

原文出处:/show_1GW3gOLnpLwF.html