reactive
源码位置:packages/reactivity/src/reactive.ts
类型:
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。
响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。
reactive与ref结合时,ref自动解包情况:访问reactive对象的ref属性值时,ref的自动解包以及响应式
const age = ref(1)
const obj = reactive({
people: {
age
}
})
// 会对ref属性值进行深层解包
console.log('obj.people.age === age.value:', obj.people.age === age.value) // true
// 先点击print按钮
const print = () => {
const { age } = obj.people
console.log('解构出来的age值:', age) // 1
console.log('obj.people.age:', obj.people.age) // 1
}
// 再点击change按钮
const change = () => {
obj.people.age++
console.log('obj.people.age:', obj.people.age) // 2
console.log('age.value:', age.value) // 2
age.value++
console.log('obj.people.age:', obj.people.age) // 3
console.log('age.value:', age.value) // 3
}
<template>
<div>
obj.people.age: {{ obj.people.age }}
</div>
<button @click="print">打印年龄</button>
<button @click="change">修改年龄</button>
</template>7
reactive与ref结合时,ref不自动解包情况:当访问到某个响应式数组或Map这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包,详细使用方法见:juejin.cn/post/730657…
若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,可以使用shallowReactive来替代。
为reactive()标注类型
- 隐式地从参数中推倒
import { reactive } from 'vue'
// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
- 在定义变量时,显示地标注类型的返回值
import { reactive } from 'vue'
interface Book {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue 3 指引' })
const age = ref(1)
// 不推荐使用,在接口中定义的age为Ref<number>类型,但是在访问obj1.people.age时,返回的数据类型为number类型
const obj1 = reactive<{
people: {
age: Ref<number>
}
}>({
people: {
age
}
})
interface People {
people: {
age: number
}
}
// age赋值为Ref<number>
const obj2: People = reactive({
people: {
age
}
})
// age赋值为number类型
const obj3: People = reactive({
people: {
age: 1
}
})
console.log(obj1.people.age) // number 1
console.log(obj2.people.age) // number 1
console.log(obj3.people.age) // number 1
不推荐使用
reactive()的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。
shallowReactive
类型:
function shallowReactive<T extends object>(target: T): T
和 reactive() 不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
const state = shallowReactive({
count: 2, // 响应式
foo: ref(1),
nested: {
bar: 2 // 非响应式
}
})
console.log('state.foo:', state.foo) // 打印Ref<1>,而非1
<!-- 自动解包,为插值语法的特殊情况 -->
<div>
{{ state.foo}}
</div>
<!-- 不会自动解包,页面显示为:[object Object]1 -->
<div>
{{ state.foo + 1}}
</div>
const reactiveState = reactive({
foo: ref(1),
nested: {
bar: 2
}
})
<!-- 1 -->
<div>{{ reactiveState.foo }}</div>
<!-- 2 -->
<div>{{ reactiveState.foo + 1 }}</div>
浅层数据结构应该只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中,因为它创建的树具有不一致的响应行为,这可能很难理解和调试。