Vue3 响应式工具函数:核心功能解析与实战指南
·
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() - 检查是否为代理
作用
检查对象是否是由 reactive 或 readonly 创建的代理。
使用方法
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 的核心,它们提供了强大的类型检查和值处理能力,使得编写可复用的组合式函数变得更加容易和安全。
更多推荐
所有评论(0)