Responsive utility functions (toRef/toRefs/isProxy, etc.)
Vue 3's reactivity system introduces several key utility functions that provide finer control over the behavior of reactive data. Functions like toRef
, toRefs
, isProxy
, and others frequently appear in the Composition API. Understanding their characteristics and use cases can significantly improve development efficiency.
toRef: Creating Reactive References
toRef
is used to create an independent ref reference from a property of a reactive object. This ref will stay synchronized with the source property—modifying the ref updates the source object, and vice versa.
import { reactive, toRef } from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
const countRef = toRef(state, 'count')
// Modifying the ref affects the source object
countRef.value++
console.log(state.count) // Outputs 1
// Modifying the source object also updates the ref
state.count = 42
console.log(countRef.value) // Outputs 42
A typical use case is maintaining reactivity when destructuring a reactive object:
// Incorrect approach: Direct destructuring loses reactivity
const { count } = state
// Correct approach: Use toRef to preserve reactivity
const count = toRef(state, 'count')
toRefs: Batch-Creating Reactive References
toRefs
is the batch version of toRef
. It converts all properties of a reactive object into a collection of ref objects.
import { reactive, toRefs } from 'vue'
const state = reactive({
x: 10,
y: 20
})
const refs = toRefs(state)
// refs is now { x: Ref<number>, y: Ref<number> }
console.log(refs.x.value) // 10
console.log(refs.y.value) // 20
toRefs
is particularly useful when returning reactive objects from composable functions:
function usePosition() {
const pos = reactive({
x: 0,
y: 0
})
// Method to update position
function update(newX, newY) {
pos.x = newX
pos.y = newY
}
// Return destructured reactive refs
return { ...toRefs(pos), update }
}
// Can destructure without losing reactivity
const { x, y, update } = usePosition()
isProxy: Checking Proxy Objects
isProxy
checks whether an object is a proxy created by reactive
or 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
This function is especially useful when developing tools or libraries to verify if parameters are the expected reactive objects.
isReactive and isReadonly
These functions provide more specific proxy type checks:
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
Note that readonly(reactive(obj))
will return true
for both isReactive
and isReadonly
.
shallowReactive and shallowRef
For scenarios where deep reactivity isn't needed, Vue provides shallow versions:
import { shallowReactive, shallowRef } from 'vue'
const shallowState = shallowReactive({
nested: {
count: 0
}
})
// Top-level properties are reactive
shallowState.nested = { count: 1 } // Triggers reactivity
// Nested properties are not reactive
shallowState.nested.count++ // Does not trigger reactivity
const shallow = shallowRef({ count: 0 })
// Changes to .value trigger reactivity
shallow.value = { count: 1 } // Triggers reactivity
// Changes to nested properties do not
shallow.value.count++ // Does not trigger reactivity
customRef: Creating Custom Refs
customRef
allows creating refs with custom getters and setters:
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)
}
}
})
}
// Usage
const text = useDebouncedRef('hello')
// Rapid consecutive updates will only trigger one change
text.value = 'h'
text.value = 'he'
text.value = 'hel'
// After 200ms, the final value updates to 'hel'
triggerRef: Forcing Updates for shallowRef
For shallowRef
, directly modifying nested properties won't trigger updates. Use triggerRef
in such cases:
import { shallowRef, triggerRef } from 'vue'
const shallow = shallowRef({ count: 0 })
// Modify nested property
shallow.value.count++
// Manually trigger update
triggerRef(shallow)
markRaw and toRaw
Sometimes, you need to ensure certain objects are never converted to proxies:
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
// Retrieve the raw object
const raw = toRaw(reactiveObj)
console.log(raw === obj) // true
This is useful when integrating third-party libraries or in performance-sensitive scenarios.
Practical Applications of Reactivity Utilities
These utility functions are particularly powerful when building composable logic. For example, implementing shared state management:
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 }
}
// Share the same state across multiple components
const store = createStore()
// Component A
const { count, double, increment } = store
// Component B
const { count: otherCount } = store
Reactivity Utilities with TypeScript
These functions work well with TypeScript, providing robust type inference:
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>
Performance Considerations
While the reactivity system is efficient, certain scenarios require attention:
- Use
shallowReactive
orshallowRef
for large reactive objects. - For frequently updated data, consider using raw values with
ref
. - For static data that won't change, use
markRaw
to avoid unnecessary proxying.
const heavyArray = markRaw([...]) // Avoid proxying arrays with thousands of elements
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn