Vue3关于响应式数据类型详解(ref、reactive、toRef、及toRefs)
Oxygen_liu 人气:0ref
接受一个内部值,返回一个响应式的、可更改的 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 。
加载全部内容