在 vue 项目中,我经常会碰到的这样的需求:一个有着表单的组件,当表单提交后,需要重置表单回到初始状态,这其实就是对组件的状态进行重置。本文就来说说在 vue3 和 vue2 项目中,如何重置组件的状态。
vue3
我们以如下所示的表单为例,初始时各个表单项都有些默认值,存储在 state 对象内,当点击重置按钮时,调用 reset 方法进行清空:
<!-- src\App.vue -->
<template>
<form @submit="submit">
<div>
<label for="name">name: </label>
<input type="text" id="name" v-model="state.name" />
</div>
<div>
<label for="age">age: </label>
<input type="number" id="age" v-model="state.age" />
</div>
<div>
<button type="submit">提交</button>
<button @click="reset">重置</button>
</div>
</form>
</template>
我们定义了一个 hook 文件 useReset.ts,来统一处理组件状态的重置。在里面定义了两个函数:
useResetRef()
useResetRef() 用于处理 Ref 对象的重置。在组件内调用 useResetRef 时,需要传入一个回调函数,该函数返回一个对象,为初始状态:
<!-- src\App.vue -->
<script setup lang="ts">
import { useResetRef } from '@/hooks/useReset'
// 提交表单
function submit(event: Event) {
event.preventDefault()
}
// 获取初始状态和重置方法
const [state, reset] = useResetRef(() => ({
name: 'Jay',
age: 22
}))
</script>
然后在 useResetRef 内,先定义状态对象 state, 值为 ref(cb()),将数据变为响应式的。然后定义 reset 方法,在里面让 state.value 的值为 cb(),即再次调用回调,生成的即为初始状态的对象:
// src\hooks\useReset.ts
import { ref } from "vue"
import type { Ref } from "vue"
export function useResetRef<T>(cb: () => T) {
const state = ref(cb()) as Ref<T>
const reset = () => {
state.value = cb()
}
return [state, reset] as const
}
最后返回值写成数组的形式是方便在组件中解构使用时可以重新命名 state 和 reset,添加 as const 则是避免 ts 的类型检测报错:
useResetReactive()
useResetReactive() 用于处理 Reactive 对象的重置:
export function useResetReactive<T extends object>(cb: () => T) {
const state = reactive(cb())
const reset = () => {
// 处理可能的新增属性
Object.keys(state).forEach(key => Reflect.deleteProperty(state, key))
Object.assign(state, cb())
}
return [state, reset] as const
}
因为 reactive() 只能接收对象或数组类型的参数,所以需要使用泛型约束 <T extends object>。对于 Reactive 对象,为了不丧失数据的响应式,我们使用 Object.assign() 来实现状态的重置。为了避免组件中可能会有增加 state 属性的操作,我们还遍历了 state 的除 Symbol 以外的自身可枚举属性,然后逐个删除。
vue2
在 vue2 项目中,响应式数据都是写在 data() 方法返回的对象中的:
export default {
data() {
return {
state: {
name: 'Jay',
age: 22
}
}
}
}
我们可以通过 this.$options.data 拿到 data 函数,比如打印 console.log(this.$options.data) 得到结果如下:
那么我们就可以在 reset() 方法中,调用 this.$options.data() 去获取 data 函数返回的这个对象,然后通过 Object.assign(),与组件数据 this.$data 进行合并,从而实现重置状态的效果:
export default {
methods: {
reset() {
Object.assign(this.$data, this.$options.data())
}
}
}
顺便说一下,vue 框架提供的 $$ 开头的变量,一般是不建议使用的。而像 $data 这种 $ 开头的变量,虽然可以使用,但尽量只是读取或附加些属性。
使用 pinia
我在实际项目中更喜欢使用 pinia 对各个组件用到的状态进行统一管理,然后就可以直接通过 pinia 提供的 $reset 方法, 重置 state 到初始化状态,比如:
appStore.$reset()
请注意,对于各个 store 的定义,pinia 提供了选项式和组合式两种类型的语法,只有采用选项式的写法时,才可以调用 store 的 $reset() 方法。
