Vue3 响应式工具函数:核心功能解析与实战指南

1. isRef() - 检查是否为 ref

作用

检查一个值是否为 ref 对象。

使用方法

import { ref, isRef, reactive } from 'vue'

// 基本使用
const count = ref(0)
const state = reactive({ name: 'John' })
const plainValue = 123

console.log(isRef(count))      // true
console.log(isRef(state))      // false
console.log(isRef(plainValue)) // false

// 在工具函数中使用
function getValue(maybeRef) {
  if (isRef(maybeRef)) {
    return maybeRef.value
  }
  return maybeRef
}

// 处理可能是 ref 的参数
function useFeature(maybeRef) {
  const value = isRef(maybeRef) ? maybeRef : ref(maybeRef)
  return {
    value,
    double: computed(() => value.value * 2)
  }
}

2. unref() - 获取 ref 的值

作用

如果参数是 ref,则返回其内部值,否则返回参数本身。相当于 isRef(val) ? val.value : val

使用方法

import { ref, unref } from 'vue'

const count = ref(10)
const name = 'John'

console.log(unref(count)) // 10
console.log(unref(name))  // 'John'

// 在组合式函数中的实际应用
function useApi(url, params = {}) {
  // 统一处理可能是 ref 的参数
  const actualUrl = unref(url)
  const actualParams = unref(params)
  
  return fetch(actualUrl, { body: JSON.stringify(actualParams) })
}

// 处理响应式或静态值
function useCounter(initialValue) {
  const count = ref(unref(initialValue) || 0)
  
  const increment = () => {
    count.value++
  }
  
  return {
    count,
    increment
  }
}

// 在模板中使用的工具函数
function formatCurrency(amount) {
  const actualAmount = unref(amount)
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(actualAmount)
}

3. toValue() - 获取响应式值(Vue 3.3+)

作用

将 ref 或 getter 函数标准化为值。如果参数是 ref,返回其值;如果是函数,调用它并返回结果;否则直接返回参数。

使用方法

import { ref, toValue, computed } from 'vue'

const count = ref(10)
const getter = () => 20
const staticValue = 30

console.log(toValue(count))       // 10
console.log(toValue(getter))      // 20
console.log(toValue(staticValue)) // 30

// 在组合式函数中的强大应用
function useFeature(source) {
  const value = computed(() => {
    // 自动处理 ref、getter 或静态值
    return toValue(source) * 2
  })
  
  return { value }
}

// 复杂示例:处理多种输入类型
function useDataLoader(loader) {
  const data = ref(null)
  const loading = ref(false)
  
  const load = async () => {
    loading.value = true
    try {
      // loader 可以是 ref、getter 函数或静态值
      const loaderFn = toValue(loader)
      data.value = await loaderFn()
    } finally {
      loading.value = false
    }
  }
  
  return { data, loading, load }
}

// 使用示例
const staticLoader = () => fetch('/api/data')
const reactiveLoader = ref(() => fetch('/api/data'))
const computedLoader = computed(() => () => fetch('/api/data'))

useDataLoader(staticLoader)     // 静态函数
useDataLoader(reactiveLoader)   // ref 包装的函数
useDataLoader(computedLoader)   // computed 返回的函数

4. toRef() - 创建属性 ref

作用

为 reactive 对象的属性创建一个 ref,保持与源属性的响应式连接。

使用方法

import { reactive, toRef } from 'vue'

const state = reactive({
  name: 'John',
  age: 25,
  address: {
    city: 'New York'
  }
})

// 创建单个属性的 ref
const nameRef = toRef(state, 'name')
const ageRef = toRef(state, 'age')

// 修改 ref 会更新原对象
nameRef.value = 'Jane'
console.log(state.name) // 'Jane'

// 原对象修改也会更新 ref
state.age = 30
console.log(ageRef.value) // 30

// 在组合式函数中使用
function useUserProperty(user, property) {
  const propertyRef = toRef(user, property)
  
  const updateProperty = (value) => {
    propertyRef.value = value
  }
  
  return {
    property: propertyRef,
    updateProperty
  }
}

// 处理可能不存在的属性
const emailRef = toRef(state, 'email' as any) // 需要类型断言

// 在模板中传递单个属性
const { name } = toRefs(state)
// 等价于
const name = toRef(state, 'name')

5. toRefs() - 转换 reactive 为 refs

作用

将 reactive 对象转换为普通对象,其中每个属性都是指向相应原始属性的 ref。

使用方法

import { reactive, toRefs } from 'vue'

const state = reactive({
  name: 'John',
  age: 25,
  profile: {
    level: 'admin'
  }
})

// 转换为 refs 对象
const refs = toRefs(state)

console.log(refs.name.value) // 'John'
console.log(refs.age.value)  // 25

// 在解构时保持响应性
const { name, age } = toRefs(state)

name.value = 'Jane' // 响应式更新
console.log(state.name) // 'Jane'

// 在组合式函数返回时的最佳实践
function useUser() {
  const state = reactive({
    name: '',
    age: 0,
    loading: false
  })
  
  const fetchUser = async () => {
    state.loading = true
    // ... 获取用户数据
    state.loading = false
  }
  
  // 返回 toRefs 以便解构保持响应性
  return {
    ...toRefs(state),
    fetchUser
  }
}

// 使用组合式函数
const { name, age, loading, fetchUser } = useUser()

// 在模板中使用
// <div>{{ name }}</div>
// <button @click="fetchUser" :disabled="loading">Fetch</button>

// 处理嵌套对象(注意:嵌套对象不会转换为 refs)
const { profile } = toRefs(state)
console.log(isRef(profile)) // true
console.log(isRef(profile.value.level)) // false - 嵌套属性不是 ref

6. isProxy() - 检查是否为代理

作用

检查对象是否是由 reactivereadonly 创建的代理。

使用方法

import { reactive, readonly, isProxy, ref } from 'vue'

const state = reactive({ count: 0 })
const readOnlyState = readonly(state)
const plainObject = { count: 0 }
const countRef = ref(0)

console.log(isProxy(state))         // true
console.log(isProxy(readOnlyState)) // true
console.log(isProxy(plainObject))   // false
console.log(isProxy(countRef))      // false

// 在工具函数中使用
function traverse(obj, seen = new Set()) {
  if (!isProxy(obj) && (obj === null || typeof obj !== 'object')) {
    return obj
  }
  
  // 避免循环引用
  if (seen.has(obj)) return
  seen.add(obj)
  
  for (const key in obj) {
    traverse(obj[key], seen)
  }
  
  return obj
}

// 安全检查
function safeAssign(target, source) {
  if (isProxy(target)) {
    console.warn('Assigning to a proxy may have unexpected behavior')
  }
  return Object.assign(target, source)
}

7. isReactive() - 检查是否为 reactive 代理

作用

检查对象是否是由 reactive 创建的响应式代理。

使用方法

import { reactive, readonly, isReactive, shallowReactive } from 'vue'

const state = reactive({ count: 0 })
const readOnlyState = readonly(state)
const shallowState = shallowReactive({ count: 0, nested: { value: 1 } })
const plainObject = { count: 0 }

console.log(isReactive(state))         // true
console.log(isReactive(readOnlyState)) // false
console.log(isReactive(shallowState))  // true
console.log(isReactive(plainObject))   // false

// 检查嵌套属性
console.log(isReactive(shallowState.nested)) // false - 浅层 reactive

// 在工具函数中的应用
function deepTraverse(obj, callback) {
  if (isReactive(obj)) {
    console.log('Traversing reactive object')
  }
  
  for (const key in obj) {
    const value = obj[key]
    callback(key, value)
    
    if (isReactive(value)) {
      deepTraverse(value, callback)
    }
  }
}

8. isReadonly() - 检查是否为只读代理

作用

检查对象是否是由 readonly 创建的只读代理。

使用方法

import { reactive, readonly, isReadonly, shallowReadonly } from 'vue'

const state = reactive({ count: 0 })
const readOnlyState = readonly(state)
const shallowReadOnlyState = shallowReadonly(state)
const plainObject = { count: 0 }

console.log(isReadonly(state))               // false
console.log(isReadonly(readOnlyState))       // true
console.log(isReadonly(shallowReadOnlyState)) // true
console.log(isReadonly(plainObject))         // false

// 在组合式函数中进行保护
function useProtectedState(initialState) {
  const state = reactive({ ...initialState })
  
  // 返回只读版本
  return readonly(state)
}

// 使用保护的状态
const protectedState = useProtectedState({ count: 0 })

// 检查是否可写
if (!isReadonly(protectedState)) {
  protectedState.count = 1 // 这行不会执行
}

// 在开发工具中使用
function debugState(state) {
  if (isReadonly(state)) {
    console.log('State is readonly - modifications will be blocked')
  } else if (isReactive(state)) {
    console.log('State is reactive - modifications are allowed')
  } else {
    console.log('State is plain object')
  }
}

综合使用示例

import { 
  ref, 
  reactive, 
  readonly, 
  isRef, 
  unref, 
  toValue, 
  toRef, 
  toRefs, 
  isProxy, 
  isReactive, 
  isReadonly 
} from 'vue'

// 综合示例函数
function analyzeValue(value) {
  console.log('Value:', value)
  console.log('isRef:', isRef(value))
  console.log('unref:', unref(value))
  console.log('toValue:', toValue(value))
  console.log('isProxy:', isProxy(value))
  console.log('isReactive:', isReactive(value))
  console.log('isReadonly:', isReadonly(value))
  console.log('---')
}

// 测试各种类型的值
const countRef = ref(10)
const state = reactive({ name: 'John' })
const readOnlyState = readonly(state)
const getter = () => 'from getter'

analyzeValue(countRef)        // ref
analyzeValue(state)           // reactive
analyzeValue(readOnlyState)   // readonly  
analyzeValue(getter)          // getter 函数
analyzeValue('static')        // 静态值

// 实用的工具函数
function normalizeToRefs(obj) {
  if (isReactive(obj) || isReadonly(obj)) {
    return toRefs(obj)
  }
  return obj
}

function getRawValue(value) {
  if (isRef(value)) return value.value
  if (typeof value === 'function') return value()
  return value
}

// 在组合式函数中的实际应用
function useFlexibleFeature(source) {
  // 处理所有类型的源数据
  const value = computed(() => {
    const rawValue = toValue(source)
    // 对原始值进行处理
    return String(rawValue).toUpperCase()
  })
  
  return { value }
}

这些工具函数是 Vue 3 组合式 API 的核心,它们提供了强大的类型检查和值处理能力,使得编写可复用的组合式函数变得更加容易和安全。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐