大宇宇宇
发布于 2025-09-03 / 5 阅读
0
0

toRef和toRefs的区别

toRef 和 toRefs的作用:

toRef: 基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。通俗来说,就是复制 reactive 里的单个属性并转成 ref,改变该属性的值将更新ref的值,反之,改变复制后的ref的值,对应的reactive里的属性的值也会改变,类似浅拷贝;

toRef应用:想要将响应式对象中的某个属性单独提供给外部使用。

toRefs: 将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用toRef()创建的。通俗来说,就是复制 reactive 里的所有属性并转成 ref,改变某属性的值将更新对应ref的值,反之,改变复制后的ref的值,对应的reactive里的属性的值也会改变;

总结:toRef复制的是reactive 里的单个属性,而toRefs复制的是reactive 里的所有属性。

toRef使用方式:

const state = reactive({
  foo: 1,
  bar: 2
})
// 复制 reactive 里的单个属性,转成 ref
const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2
console.log(fooRef.value) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(state.foo) // 3
console.log(fooRef.value) // 3

toRef()就相当于一个中间件,将属性转换成响应式数据,但一次只能处理一个;

注意:toRef不同于ref:

const fooRef = ref(state.foo);

上面这个 ref 不会state.foo 保持同步,因为这个 ref() 接收到的是一个纯数值。

toRef() 这个函数在你想把一个 prop 的 ref 传递给一个组合式函数时会很有用:(上文提到过的toRef应用)

<script setup>
import { toRef } from 'vue'

const props = defineProps(/* ... */)

// 将 `props.foo` 转换为 ref,然后传入
// 一个组合式函数
useSomeFeature(toRef(props, 'foo'))
</script>

toRef 与组件 props 结合使用时,关于禁止对 props 做出更改的限制依然有效。尝试将新的值传递给 ref 等效于尝试直接更改 props,这是不允许的。在这种场景下,你可能可以考虑使用带有 getsetcomputed 替代。

即使源属性当前不存在,toRef() 也会返回一个可用的 ref。这让它在处理可选 props 的时候格外实用,相比之下 toRefs 就不会为可选 props 创建对应的 refs。

toRefs使用方式:

const state = reactive({
  foo: 1,
  bar: 2
})
// 复制 reactive 里的所有属性
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// 这个 ref 和源属性已经“链接上了”
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

可以看出,功能与toRef一致,但是可以批量处理多个ref对象。

当从组合式函数中返回响应式对象时,toRefs相当有用。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性:

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // ...基于状态的操作逻辑

  // 在返回时都转为 ref
  return toRefs(state)
}

// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()

toRefs在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用toRef

最后注意:这两个属性作用是一样的,但是访问方式不同

  • template 要想访问 toRefs 的值,需要带上 .value 如果不带上,就会出现双引号。

  • template 要想访问 toRef 的值,不需要带上 .value

<template>
  <h2>
    reactive-toRef: {{ info.msg }}
    reactive-toRefs: {{ info1.msg }} 
  </h2>
  <h2>
    // 不需要带上 .value
    toRef: {{ msgCopy }}
  </h2>
  <h2>
    // 需要带上 .value
    toRefs: {{ infoCopy.msg.value }}
  </h2>
  <button @click="onChangeMsg">更换 toRef</button>
  <button @click="onChangeInfo">更换 toRefs</button>
</template>

<script>
import { reactive, toRef, toRefs } from 'vue'
export default {
    setup() {
        let info = reactive({
            name: 'Tony',
            msg: 'Hello'
        })
        let info1 = reactive({
            name: 'Tony1',
            msg: 'Hello1'
        })
	// 复制 info 里的 msg 属性  针对单个属性
        let msgCopy = toRef(info, 'msg')
        // 复制整个info  针对所有属性
        let infoCopy = toRef(info1);
        // 更改 msgCopy
        const onChangeMsg = () => {
            msgCopy.value = 'world!'
        }
        // 更改 infoCopy.msg
        const onChangeInfo = () => {
            infoCopy.msg.value = 'world1!'
        }
        return {
            info,
            info1,
            msgCopy,
            infoCopy,
            onChangeMsg,
            onChangeInfo
        }
    }
}
</script>


评论