引言
在管理系统项目中,查询表单是用户与系统交互的核心界面,无论是在客户关系管理(CRM)、企业资源规划(ERP)系统,还是项目管理工具中。它们让用户能够根据特定条件检索、分析和操作数据,极大提升了工作效率和决策质量。
但随着查询表单项的增加和复杂化,传统的手动重置方法——逐个属性重置——变得既繁琐又容易出错。开发者需要不断核对每个字段的初始值,以确保在重置时能准确恢复到初始状态,这不仅增加了开发和维护的工作量,也提高了出错的风险。
为了简化这一流程,本文将探讨如何利用Vue 3的Composition API实现useResettableRef和useResettableReactive两个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;
}
思路
- 克隆初始状态:
- 在定义
useResettable函数时,首先利用深度克隆技术复制传入的初始值对象,以确保有一个精确的初始状态副本。
- 在定义
- 响应式状态管理:
- 利用Vue 3的ref或reactive API来创建一个响应式状态对象,这样可以确保状态的变化能够被Vue的响应式系统捕捉并更新到视图层。
- 实现重置功能:
- 设计一个重置函数,它可以接受无参数或特定字段作为参数。无参数时,该函数将重置整个响应式对象到初始状态;若提供字段参数,则仅重置这些指定字段到初始值。
提问🙋:
为什么useResettableRef重置整个响应式对象,使用state.value直接赋值,而useResettableReactive需要遍历清空响应式对象再合并?
因为ref包装的是一个值,而reactive包装的是一个对象。直接替换ref的值不会影响到Vue的响应式系统,但是直接替换reactive对象会替换掉整个响应式对象导致Vue失去对原对象的响应式跟踪 - 增强灵活性和扩展性:
useResettable函数支持传入自定义的克隆函数,这使得开发者能够处理特殊类型数据的克隆需求,例如Date对象或函数,或者在克隆过程中执行特定的自定义操作。
- 使用示例:
- 在Vue组件中,只需传入表单的初始值即可使用
useResettable。在模板中直接使用响应式状态对象,并在需要时调用重置函数,即可轻松将表单恢复到初始状态。
- 在Vue组件中,只需传入表单的初始值即可使用
<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>
感谢阅读,敬请斧正!