关于Vue3响应式之toRef、toRefs不得不知的使用技巧

3,369 阅读3分钟

1、为源响应式对象上的某个属性新创建一个ref,保持响应式连接

<script setup lang="ts">
    import { ref, Ref, reactive } from "vue"
    const state = reactive({
        foo: 1,
        bar: 2,
    })
    const fooRef = ... // 修改这里的实现
​
    // 修改引用将更新原引用
    fooRef.value++
    console.log(state.foo === 2)
​
    // 修改原引用也会更新`ref`
    state.foo++
    console.log(fooRef.value === 3)
</script>

如上所述,...的内容要写上什么,才能保持fooRefstate.foo的响应式连接。先试着写一个const fooRef = ref(state.foo),结果发现是不可行的,ref() 函数可以将原始值转换为响应式对象之外,但不能将源响应式对象的某个 property 转换为一个普通的响应式引用,从而保持引用和源对象的响应式连接。针对这个问题我们可以通过 toRef() 函数来实现。

<script setup lang="ts">
    import { ref, Ref, reactive,toRef } from "vue"
    const state = reactive({
        foo: 1,
        bar: 2,
    })
    const fooRef = toRef(state,'foo')
​
    // 修改引用将更新原引用
    fooRef.value++
    console.log(state.foo === 2)
​
    // 修改原引用也会更新`ref`
    state.foo++
    console.log(fooRef.value === 3)
</script>

要点:ref() 函数将原始值转换为包含一个 value 属性的响应式对象,而 toRef() 函数则可以将源响应式对象的某个 property 转换为一个普通的响应式引用。

2、解构/扩展对象,导致响应式丢失解决办法

<script setup lang="ts">
import { reactive } from "vue"function useCount() {
  const state = reactive({
    count: 0,
  })
​
  function update(value: number) {
    state.count = value
  }
​
  return {
    state,
    update,
  }
}
​
// 确保解构不丢失响应性
const { state: { count }, update } = useCount()
​
</script><template>
  <div>
    <p>
      <span @click="update(count-1)">-</span>
      {{ count }}
      <span @click="update(count+1)">+</span>
    </p>
  </div>
</template>

运行上述代码,我们会发现当我们点击更新方法,界面上的数值并不会随之改变,这是因为我们在解构之后丢失了响应式,这是因为解构赋值进行一次浅拷贝,而state中的count并不是引用类型,所以countstate.count并不是指向同一个地址,响应式也就失效了。

<script setup lang="ts">
import { reactive } from "vue"function useCount() {
  const state = reactive({
    count: 0,
  })
​
  function update(value: number) {
    state.count = value
  }
​
  return {
    state:toRefs(state),
    update,
  }
}
​
// 确保解构不丢失响应性
const { state: { count }, update } = useCount()
​
</script><template>
  <div>
    <p>
      <span @click="update(count-1)">-</span>
      {{ count }}
      <span @click="update(count+1)">+</span>
    </p>
  </div>
</template>

toRefs()函数可以将响应式对象转换为普通对象,但该普通对象中的每个属性都是一个单独的ref对象。注意这个词哈对象(引用类型),这个时候我们进行解构(浅拷贝)之后,指向的还是同一个地址哦,因此响应式也不会丢失了。

3、进一步了解一下toReftoRefs这两个函数

简单概述:

toRef的作用是将响应式对象的一个属性转化为一个 ref 对象;它接收两个参数,第一个参数是响应式对象,第二个参数是属性字符串,表示需要转化为 ref 对象的属性;

toRefs的作用是将响应式对象转化为一个普通对象,并通过 toef 转化出每个属性的响应式引用。

注:两者都只能对响应式对象进行转换。toRefs 在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef。另外,toRefs 返回的对象是响应式而不是深度嵌套响应式,只有对象自身的引用是响应式的。如果我们需要响应式地访问深度嵌套属性,还需要使用其他的方法来实现。

使用场景:

toRef 的使用场景经常出现在父子组件之间传递数据的过程中。例如,父组件传递了一个对象给子组件,并不想让子组件直接操作这个对象,可以将子组件需要的属性用 toRef 转化为 ref 对象,让子组件操作这个引用对象来达到修改父组件数据的目的。这样可以避免子组件直接修改父组件传递的数据,增加代码的可维护性和可读性。

toRefs 的使用场景经常出现在当从组合式函数中返回响应式对象时,使用它,消费者组件可以解构/展开返回的对象而不会失去响应性,这在我们自定义hook的时候经常能使用到。

最后看到这里的小伙伴,我推荐一个Vue.js挑战的在线编程网站,可用于查缺补漏,巩固相关知识,好东西,当然要分享啦。

[地址]  cn-vuejs-challenges.netlify.app/

image-20230423104826380.png