阿里云主机折上折
  • 微信号
Current Site:Index > Responsive utility functions (toRef/toRefs/isProxy, etc.)

Responsive utility functions (toRef/toRefs/isProxy, etc.)

Author:Chuan Chen 阅读数:55651人阅读 分类: Vue.js

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:

  1. Use shallowReactive or shallowRef for large reactive objects.
  2. For frequently updated data, consider using raw values with ref.
  3. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.