响应式工具函数(toRef/toRefs/isProxy等)
Vue 3 的响应式系统带来了几个关键工具函数,它们能更精细地控制响应式数据的行为。toRef
、toRefs
、isProxy
等函数在组合式 API 中频繁出现,理解它们的特性和使用场景能显著提升开发效率。
toRef:创建响应式引用
toRef
用于从响应式对象的属性创建独立的 ref 引用。这个 ref 会与源属性保持同步,修改 ref 会更新源对象,反之亦然。
import { reactive, toRef } from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
const countRef = toRef(state, 'count')
// 修改 ref 会影响源对象
countRef.value++
console.log(state.count) // 输出 1
// 修改源对象也会更新 ref
state.count = 42
console.log(countRef.value) // 输出 42
典型使用场景是在解构响应式对象时保持响应性:
// 错误做法:直接解构会丢失响应性
const { count } = state
// 正确做法:使用 toRef 保持响应性
const count = toRef(state, 'count')
toRefs:批量创建响应式引用
toRefs
是 toRef
的批量版本,它会将响应式对象的所有属性转换为 ref 对象集合。
import { reactive, toRefs } from 'vue'
const state = reactive({
x: 10,
y: 20
})
const refs = toRefs(state)
// refs 现在是 { x: Ref<number>, y: Ref<number> }
console.log(refs.x.value) // 10
console.log(refs.y.value) // 20
在组合式函数中返回响应式对象时,toRefs
特别有用:
function usePosition() {
const pos = reactive({
x: 0,
y: 0
})
// 更新位置的方法
function update(newX, newY) {
pos.x = newX
pos.y = newY
}
// 返回解构后的响应式引用
return { ...toRefs(pos), update }
}
// 使用时可以解构而不丢失响应性
const { x, y, update } = usePosition()
isProxy:检查代理对象
isProxy
用于检查一个对象是否是由 reactive
或 readonly
创建的代理。
import { reactive, readonly, isProxy } from 'vue'
const reactiveObj = reactive({})
const readonlyObj = readonly({})
const plainObj = {}
console.log(isProxy(reactiveObj)) // true
console.log(isProxy(readonlyObj)) // true
console.log(isProxy(plainObj)) // false
这个函数在开发工具或库时特别有用,可以用来验证参数是否为预期的响应式对象。
isReactive 和 isReadonly
这两个函数提供了更具体的代理类型检查:
import { reactive, readonly, isReactive, isReadonly } from 'vue'
const reactiveObj = reactive({})
const readonlyObj = readonly({})
const readonlyReactive = readonly(reactive({}))
console.log(isReactive(reactiveObj)) // true
console.log(isReactive(readonlyReactive)) // true
console.log(isReadonly(readonlyObj)) // true
console.log(isReadonly(readonlyReactive)) // true
注意 readonly(reactive(obj))
会同时返回 isReactive
和 isReadonly
为 true。
shallowReactive 和 shallowRef
对于不需要深度响应式的场景,Vue 提供了浅层响应式版本:
import { shallowReactive, shallowRef } from 'vue'
const shallowState = shallowReactive({
nested: {
count: 0
}
})
// 顶层属性是响应式的
shallowState.nested = { count: 1 } // 触发响应
// 嵌套属性不是响应式的
shallowState.nested.count++ // 不会触发响应
const shallow = shallowRef({ count: 0 })
// .value 的变化会触发响应
shallow.value = { count: 1 } // 触发响应
// 嵌套属性的变化不会
shallow.value.count++ // 不会触发响应
customRef:创建自定义 ref
customRef
允许创建具有自定义 getter 和 setter 的 ref:
import { customRef } from 'vue'
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
// 使用
const text = useDebouncedRef('hello')
// 快速连续修改只会触发一次更新
text.value = 'h'
text.value = 'he'
text.value = 'hel'
// 200ms 后最终值会更新为 'hel'
triggerRef:强制触发 shallowRef 更新
对于 shallowRef
,直接修改嵌套属性不会触发更新,这时可以使用 triggerRef
:
import { shallowRef, triggerRef } from 'vue'
const shallow = shallowRef({ count: 0 })
// 修改嵌套属性
shallow.value.count++
// 手动触发更新
triggerRef(shallow)
markRaw 和 toRaw
有时我们需要确保某些对象永远不会被转为代理:
import { reactive, markRaw, toRaw } from 'vue'
const obj = { foo: 1 }
const marked = markRaw(obj)
const reactiveObj = reactive({ nested: marked })
console.log(isReactive(reactiveObj.nested)) // false
// 获取原始对象
const raw = toRaw(reactiveObj)
console.log(raw === obj) // true
这在集成第三方库或性能敏感场景下很有用。
响应式工具函数的实际应用
这些工具函数在构建可组合逻辑时特别强大。例如,实现一个共享状态管理:
import { reactive, toRefs, computed } from 'vue'
function createStore() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return { ...toRefs(state), increment }
}
// 在多个组件中共享同一状态
const store = createStore()
// 组件A
const { count, double, increment } = store
// 组件B
const { count: otherCount } = store
响应式工具与 TypeScript
这些工具函数与 TypeScript 配合良好,能提供完善的类型推断:
import { reactive, toRef } from 'vue'
interface User {
id: number
name: string
}
const user = reactive<User>({
id: 1,
name: 'Alice'
})
const nameRef = toRef(user, 'name') // Ref<string>
性能考量
虽然响应式系统很高效,但在某些情况下需要注意:
- 大型响应式对象使用
shallowReactive
或shallowRef
- 频繁更新的数据考虑使用原始值 +
ref
- 不变的静态数据使用
markRaw
避免不必要的代理
const heavyArray = markRaw([...]) // 避免数千个元素的数组被代理
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn