稀土掘金技术社区 03月08日
提升 Vue 项目开发效率:用 useWatchFields 轻松监听指定字段变化
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

在Vue 3开发中,针对复杂应用中响应式状态管理的痛点,特别是当state对象庞大时,传统watch和computed API显得繁琐。useWatchFields自定义Hook应运而生,旨在更高效地监听和响应指定字段的变化。它完全基于Vue内置API实现,无需外部库依赖,能灵活监听state中的特定字段,并自动传递变化前后值给回调函数。该Hook还支持防抖功能,避免频繁触发事件,适用于表单处理、动态数据更新和条件渲染等多种场景,极大地简化了代码结构,提高了开发效率。

🎯 **灵活指定监听字段**:通过传入字段名数组,`useWatchFields` 自动监听这些字段的变化,无需处理整个 `state` 对象。

⚙️ **高效的事件触发机制**:通过 `ref` 手动管理事件监听器,使得事件管理更加轻量。

ℹ️ **获取字段的变化信息**:当字段发生变化时,`useWatchFields` 会返回一个对象,包含哪些字段发生了变化、变化前后的值等详细信息,帮助你更精准地捕捉字段的变动。

⏱️ **防抖功能**:`useWatchFields` 支持防抖功能,避免频繁触发事件。例如,在表单输入或快速变化的字段中,防抖能够有效地减少不必要的性能消耗。可以通过传递 `debounceTimeout` 来控制防抖的延迟,单位为毫秒。

原创 ErvinHowell 2025-03-08 09:01 重庆

点击关注公众号,“技术干货” 及时达!

Vue 3 的开发中,随着应用变得越来越复杂,响应式状态管理成为了不可或缺的一部分。尤其是当你的 state 对象越来越庞大时,你可能只关心其中某些字段的变化。这时,传统的 watchcomputed API 在使用上可能显得有些繁琐且不够灵活。

为了帮助开发者更高效地监听和响应字段的变化,我们推出了一个全新的 Vue 3 自定义 Hook —— useWatchFields。这个 Hook 可以让你灵活地监听 state 中的某些指定字段变化,并且不依赖于任何外部库,完全通过 Vue 内置的 API 来实现事件的管理。

为什么使用 useWatchFields?

当你在开发中只对部分字段的变化感兴趣时,往往需要为每个字段单独设置 watch,但这样做不仅代码冗长,而且不够灵活。useWatchFields 可以帮助你精确监听特定字段的变化,并自动将变化的前后值传递给回调函数。

如何使用 useWatchFields?

假设你有一个包含多个字段的 state 对象,并且你只关心其中某些字段的变化,例如 nameage 字段。使用 useWatchFields 后,你可以像下面这样轻松实现:

import { ref } from 'vue'import { useWatchFields } from './useWatchFields'
// 假设你的 state 包含多个字段const state = ref({ name: 'John Doe', age: 30, email: 'john@example.com',})
// 监听 name 和 age 字段的变化const { onChange } = useWatchFields(state, ['name', 'age'])
// 注册事件监听器,获取字段变化信息onChange((data) => { console.log('变化字段:', data.changedFields) // ['name', 'age'] console.log('字段变化前后值:', data.fieldChangeMap)})

通过简单的几行代码,你就可以开始监听字段的变化,并且在变化时获得详细的字段变化信息!

useWatchFields 的核心功能

    灵活指定监听字段:

只需传入字段名数组,useWatchFields 会自动监听这些字段的变化,不需要处理整个 state 对象。

    高效的事件触发机制:通过 ref 手动管理事件监听器,使得事件管理更加轻量。

    获取字段的变化信息:

    当字段发生变化时,useWatchFields 会返回一个对象,包含哪些字段发生了变化、变化前后的值等详细信息,帮助你更精准地捕捉字段的变动。

    防抖功能

    useWatchFields 还支持防抖功能,避免频繁触发事件。例如,在表单输入或快速变化的字段中,防抖能够有效地减少不必要的性能消耗。你可以通过传递 debounceTimeout 来控制防抖的延迟,单位为毫秒。

const { onChange } = useWatchFields(state, ['name', 'age'], { debounceTimeout: 500 })

适用场景

无论是小型应用还是大型复杂项目中的数据管理,useWatchFields 都能为你带来极大的便利。它让你可以灵活监听 state 中指定字段的变化,避免冗长的 watch 代码,并提供详细的变化信息。直接使用 Vue 内置的 API,代码更加简洁、轻量。

源码

import { isReactive, isRef, ref, watch } from 'vue'
// 定义返回对象的类型export interface WatchFieldsResult<T> { /** 发生变化的字段数组 */ changedFields: Array<keyof T> /** 所有监听的字段数组 */ fields: Array<keyof T> /** 每个字段的变化前后值 */ fieldChangeMap: Partial<Record<keyof T, { newValue: T[keyof T] | undefined, oldValue: T[keyof T] | undefined }>>}
/** * 自定义 Hook,用于监听 `state` 中指定字段的变化。 * * @params state - 一个 `Ref` 或 `reactive` 类型的响应式对象,它的字段值会被监听。 * @params fields - 一个字符串数组,指定要监听的字段名。 * @params options.debounceDelay - 防抖延迟时间,单位毫秒。默认为 300ms,传递 `undefined` 或 `0` 时关闭防抖 * @params options.immediate - 是否在初始化时立即执行一次回调函数,默认为 `false`。 * * @returns onChange - 一个触发事件的函数,可以用于获取字段变化的详细信息。 */export function useWatchFields<T extends object>( state: Ref<T> | T, fields: Array<keyof T>, options?: { /** 可选参数,防抖延迟时间 */ debounceDelay?: number /** 初始化执行 */ immediate?: boolean },): { onChange: (listener: (data: WatchFieldsResult<T>) => void) => void } { const listeners = ref<((data: WatchFieldsResult<T>) => void)[]>([]) // 存储监听回调函数 let debounceTimeout: ReturnType<typeof setTimeout> | null = null // 防抖定时器
// 手动触发事件的函数 const trigger = (data: WatchFieldsResult<T>) => { listeners.value.forEach(listener => listener(data)) }
// 防抖函数:延迟触发 const debouncedTrigger = (data: WatchFieldsResult<T>) => { if (debounceTimeout) { // 清除上一个定时器 clearTimeout(debounceTimeout) } // 设置新的定时器 debounceTimeout = setTimeout(() => trigger(data), options!.debounceDelay!) }
// 使用 Vue 的 watch API 监听多个字段的变化 watch( () => fields.map((field) => { if (isRef(state)) { // 如果是 Ref 类型,返回对应字段的值 return state.value[field] } else if (isReactive(state)) { // 如果是 reactive 类型,返回对应字段的值 return state[field] } return undefined }), (newValues, oldValues) => { // 用于存储发生变化的字段 const changedFields: (keyof T)[] = [] // 用于存储每个字段的变化前后值 const fieldChangeMap: WatchFieldsResult<T>['fieldChangeMap'] = {}
newValues.forEach((newValue, index) => { // 获取当前字段 const field = fields[index] // 获取字段的旧值 const oldValue = oldValues[index]
// 如果新值与旧值不同,表示字段发生了变化 if (newValue !== oldValue) { changedFields.push(field) fieldChangeMap[field] = { newValue, oldValue } } })
const data: WatchFieldsResult<T> = { changedFields, fields, fieldChangeMap, }
// 根据 debounceDelay 的值判断是否启用防抖 if (options?.debounceDelay && options?.debounceDelay > 0) { debouncedTrigger(data) // 启用防抖 } else { trigger(data) // 直接触发,不启用防抖 } }, { deep: true, immediate: options?.immediate }, )
// 清理防抖定时器 onUnmounted(() => { if (debounceTimeout) { clearTimeout(debounceTimeout) // 组件销毁时清除定时器 } })
// 返回一个注册回调的函数 return { onChange: (listener: (data: WatchFieldsResult<T>) => void) => { listeners.value.push(listener) }, }}

在下次开发时,不妨尝试一下 useWatchFields,它将是你提高开发效率、简化代码的得力助手。

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Vue3 useWatchFields 自定义Hook 响应式状态管理
相关文章