前端技巧分享: 在Vue项目中如何优雅地实现查询表单的重置功能?

544 阅读4分钟

引言

在管理系统项目中,查询表单是用户与系统交互的核心界面,无论是在客户关系管理(CRM)、企业资源规划(ERP)系统,还是项目管理工具中。它们让用户能够根据特定条件检索、分析和操作数据,极大提升了工作效率和决策质量。
但随着查询表单项的增加和复杂化,传统的手动重置方法——逐个属性重置——变得既繁琐又容易出错。开发者需要不断核对每个字段的初始值,以确保在重置时能准确恢复到初始状态,这不仅增加了开发和维护的工作量,也提高了出错的风险。
为了简化这一流程,本文将探讨如何利用Vue 3Composition API实现useResettableRefuseResettableReactive两个Hook,自动化和优化表单数据的重置过程。这种方法不仅能减少冗余代码,提高代码的可维护性,还能确保重置操作的准确性和一致性,从而为开发者带来更高效、更可靠的解决方案。

效果体验

实现

1. useResettableRef Hook 实现

import { ref } from "vue";

/**
 * 使用JSON实现深克隆
 * @param value T
 * @returns T
 */
function defaultDeepClone<T>(value: T): T {
  if (value === null || typeof value !== "object") return value;
  return JSON.parse(JSON.stringify(value));
}

/**
 * 实现useResettableRef Hook
 * @param value T 初始值
 * @param deepClone (value: T)=>T 深克隆初始值
 * @returns [T, (key?: keyof T | Array<keyof T>) => void] 返回一个数组,第一个元素是响应式引用,第二个元素是重置函数
 * rawUrl: https://code.juejin.cn/api/raw/7443467451834040372?id=7443467451834056756
 */
export function useResettableRef<T>(value: T, deepClone: (value: T) => T = defaultDeepClone) {
  const initialValue = deepClone(value);
  const state = ref<T>(initialValue);
  const reset = (key?: keyof T | Array<keyof T>) => {
    const deepCloneValue = deepClone(value);
    if (key == undefined || deepCloneValue === null || typeof deepCloneValue !== "object") {
      // ref 声明值类型 传key 是无效的
      state.value = deepCloneValue;
      return;
    }

    if (typeof key === "string") {
      if (key in deepCloneValue) {
          state.value[key] = deepCloneValue[key];
      }
      return;
    };

    if (Array.isArray(key)) {
      key.forEach(k => {
        if (k in deepCloneValue) {
          state.value[k] = deepCloneValue[k];
        }
      });
      return;
    }
  };
  return [state, reset] as const;
}

2. useResettableReactive Hook 实现

import { reactive } from "vue";

/**
 * 使用JSON实现深克隆
 * @param value T
 * @returns T
 */
function defaultDeepClone<T>(value: T): T {
  if (value === null || typeof value !== "object") return value;
  return JSON.parse(JSON.stringify(value));
}

 /**
 * 实现useResettableReactive Hook
 * @param value T 初始值
 * @param deepClone (value: T)=>T 深克隆初始值
 * @returns [T, (key?: keyof T | Array<keyof T>) => void] 返回一个数组,第一个元素是响应式引用,第二个元素是重置函数
 * rawUrl: https://code.juejin.cn/api/raw/7443467117309591592?id=7443467117309607976
 */
export function useResettableReactive<T extends Record<keyof T, unknown>>(value: T, deepClone: (value: T) => T = defaultDeepClone) {
  const initialValue = deepClone(value);
  const state = reactive(initialValue) as T;
  const reset = (key?: keyof T | Array<keyof T>) => {
    const deepCloneValue = deepClone(value);
    if (key == undefined) {
      // 避免开发过程中, 使用state未初始声明的key,所以重置的时候,先清空state对象
      Object.entries(state).forEach(([key, value])=>{
        delete state[key]
      })
      // 再将deepCloneValue合并到state
      Object.assign(state, deepCloneValue);
      return;
    }

    if (typeof key === "string") {
      if (key in deepCloneValue) {
          state[key] = deepCloneValue[key];
      }
      return;
    };

    if (Array.isArray(key)) {
      key.forEach(k => {
        if (k in deepCloneValue) {
          state[k] = deepCloneValue[k];
        }
      });
      return;
    }
  };
  return [state, reset] as const;
}

思路

  1. 克隆初始状态
    • 在定义useResettable函数时,首先利用深度克隆技术复制传入的初始值对象,以确保有一个精确的初始状态副本。
  2. 响应式状态管理
    • 利用Vue 3refreactive API来创建一个响应式状态对象,这样可以确保状态的变化能够被Vue的响应式系统捕捉并更新到视图层。
  3. 实现重置功能
    • 设计一个重置函数,它可以接受无参数或特定字段作为参数。无参数时,该函数将重置整个响应式对象到初始状态;若提供字段参数,则仅重置这些指定字段到初始值。

    提问🙋:
    为什么useResettableRef重置整个响应式对象,使用state.value直接赋值,而useResettableReactive需要遍历清空响应式对象再合并?
    因为ref包装的是一个值,而reactive包装的是一个对象。直接替换ref的值不会影响到Vue的响应式系统,但是直接替换reactive对象会替换掉整个响应式对象导致Vue失去对原对象的响应式跟踪

  4. 增强灵活性和扩展性
    • useResettable函数支持传入自定义的克隆函数,这使得开发者能够处理特殊类型数据的克隆需求,例如Date对象或函数,或者在克隆过程中执行特定的自定义操作。
  5. 使用示例
    • Vue组件中,只需传入表单的初始值即可使用useResettable。在模板中直接使用响应式状态对象,并在需要时调用重置函数,即可轻松将表单恢复到初始状态。
<script lang="ts" setup>
import { useResettableRef } from './useResettableRef'

const [state, reset] = useResettableRef('useResettableRef Hook for Vue 3!');

</script>

<template>

<input v-model="state" />
<button @click="reset()">重置</button>

</template>
<script lang="ts" setup>
import { useResettableReactive } from './useResettableReactive'

const [state, reset] = useResettableReactive({ message: 'useResettableReactive Hook for Vue 3!' });

</script>

<template>

<input v-model="state.message" />
<button @click="reset()">重置</button>

</template>

感谢阅读,敬请斧正!