提升 Vue 项目开发效率:用 useWatchFields 轻松监听指定字段变化
点击关注公众号,“技术干货” 及时达!
在 Vue 3 的开发中,随着应用变得越来越复杂,响应式状态管理成为了不可或缺的一部分。尤其是当你的 state 对象越来越庞大时,你可能只关心其中某些字段的变化。这时,传统的 watch 和 computed API 在使用上可能显得有些繁琐且不够灵活。
为了帮助开发者更高效地监听和响应字段的变化,我们推出了一个全新的 Vue 3 自定义 Hook —— useWatchFields。这个 Hook 可以让你灵活地监听 state 中的某些指定字段变化,并且不依赖于任何外部库,完全通过 Vue 内置的 API 来实现事件的管理。
为什么使用 useWatchFields?
当你在开发中只对部分字段的变化感兴趣时,往往需要为每个字段单独设置 watch,但这样做不仅代码冗长,而且不够灵活。useWatchFields 可以帮助你精确监听特定字段的变化,并自动将变化的前后值传递给回调函数。
如何使用 useWatchFields?
假设你有一个包含多个字段的 state 对象,并且你只关心其中某些字段的变化,例如 name 和 age 字段。使用 useWatchFields 后,你可以像下面这样轻松实现:
import { ref } from 'vue'import { useWatchFields } from './useWatchFields'// 假设你的 state 包含多个字段const state = ref({name: 'John Doe',age: 30,email: '[email protected]',})// 监听 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可以大大简化你的代码结构。动态数据更新:例如在用户数据管理、购物车状态等场景中,你可以使用 useWatchFields精确地监听特定字段的变化。条件渲染:当你需要根据某些字段的变化来触发视图更新时,这个 Hook也能帮助你高效完成。
无论是小型应用还是大型复杂项目中的数据管理,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,它将是你提高开发效率、简化代码的得力助手。
点击关注公众号,“技术干货” 及时达!