vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3响应式数据类型

Vue3关于响应式数据类型详解(ref、reactive、toRef、及toRefs)

作者:Oxygen_liu

这篇文章主要介绍了Vue3关于响应式数据类型(ref、reactive、toRef、以及toRefs),本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

ref

接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的 property .value。

类型

function ref<T>(value: T): Ref<UnwrapRef<T>>

interface Ref<T> {
  value: T
}

详细信息

ref 对象是可更改的,也就是说你可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪,并且写操作会触发与之相关的副作用。
如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。
若要避免这种深层次的转换,请使用 shallowRef() 来替代

示例

import { ref } from 'vue
const count = ref<number>(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1

reactive

返回一个对象的响应式代理

类型

function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

详细信息

响应式转换是“深层”的:它会影响到所有嵌套的 property。一个响应式对象也将深层地解包任何 ref property,同时保持响应性。

值得注意的是,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。

返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免依赖于原始对象。

示例

import { reactive } from 'vue'
interface Obj {
  count: number
}
const obj = reactive<Obj>({ count: 0 })
obj.count++

ref 的解包:

const count = ref(1)
const obj = reactive({ count })

// ref 会被解包
console.log(obj.count === count.value) // true

// 会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2

// 也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3

注意当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

将一个 ref 赋值给为一个 reactive 属性时,该 ref 会被自动解包:

const count = ref(1)
const obj = reactive({})

obj.count = count

console.log(obj.count) // 1
console.log(obj.count === count.value) // true

toRef()

可用于为响应式对象上的 property 创建 ref。这样创建的 ref 与其源 property 保持同步:改变源 property 将更新 ref,反之亦然。

类型

function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K,
  defaultValue?: T[K]
): ToRef<T[K]>

type ToRef<T> = T extends Ref ? T : Ref<T>

示例

const state = reactive({
  foo: 1,
  bar: 2
})
const fooRef = toRef(state, 'foo')
// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2
// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

请注意,这不同于:

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

//toRef() 这个函数在你想把一个 prop 的 ref 传递给一个组合式函数时会很有用
<script setup>
import { toRef } from 'vue'

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

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

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

即使源 property 当前不存在,toRef() 也会返回一个可用的 ref。这让它在处理可选 prop 的时候格外实用,而可选 prop 在使用 toRefs 时不会被保留。

toRefs()

将一个响应式对象转换为一个普通对象,这个普通对象的每个 property 都是指向源对象相应 property 的 ref。每个单独的 ref 都是使用 toRef() 创建的。

类型

function toRefs<T extends object>(
  object: T
): {
  [K in keyof T]: ToRef<T[K]>
}

type ToRef = T extends Ref ? T : Ref<T>

示例

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

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

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

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

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

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

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

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

到此这篇关于Vue3关于响应式数据类型(ref、reactive、toRef、以及toRefs)的文章就介绍到这了,更多相关Vue3响应式数据类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文