Vue3 Hooks实战:5个高频业务场景的封装技巧(含代码示例)
如果你已经熟悉Vue3的基础语法,但在实际项目中面对重复的业务逻辑时,仍然在复制粘贴代码,那么这篇文章就是为你准备的。今天我们不谈那些老生常谈的“什么是Hooks”或者“为什么要用Hooks”,而是直接切入实际开发中最常见的五个业务场景,看看如何用Composition API将它们封装成可复用的Hooks。
我在最近的一个后台管理系统中,发现团队里不同开发者对同一个功能——比如表单验证——竟然有五种不同的实现方式。这不仅增加了维护成本,还导致新成员接手时一头雾水。正是这种经历让我意识到,一套标准化的Hooks封装方案有多么重要。
1. 表单处理:从混乱到优雅的蜕变
表单处理几乎是每个前端项目都绕不开的痛点。传统的做法是在每个表单组件里重复编写验证逻辑、提交处理和重置逻辑,代码冗余且难以维护。让我们看看如何用Hooks来改变这一现状。
1.1 基础表单Hook设计
先从一个最简单的表单Hook开始。假设我们有一个用户注册表单,包含用户名、邮箱和密码三个字段:
// useForm.js
import { reactive, toRefs } from 'vue'
export function useForm(initialValues = {}) {
const formState = reactive({
values: { ...initialValues },
errors: {},
isSubmitting: false,
isDirty: false
})
// 更新表单值
const setFieldValue = (field, value) => {
formState.values[field] = value
formState.isDirty = true
}
// 批量更新
const setValues = (newValues) => {
Object.assign(formState.values, newValues)
formState.isDirty = true
}
// 重置表单
const resetForm = () => {
Object.keys(formState.values).forEach(key => {
formState.values[key] = initialValues[key] || ''
})
formState.errors = {}
formState.isSubmitting = false
formState.isDirty = false
}
// 提交表单
const submitForm = async (submitFn) => {
if (formState.isSubmitting) return
formState.isSubmitting = true
try {
await submitFn(formState.values)
} catch (error) {
// 处理提交错误
console.error('表单提交失败:', error)
} finally {
formState.isSubmitting = false
}
}
return {
...toRefs(formState),
setFieldValue,
setValues,
resetForm,
submitForm
}
}
在组件中使用这个Hook:
<template>
<form @submit.prevent="handleSubmit">
<input
v-model="values.username"
@input="setFieldValue('username', $event.target.value)"
:class="{ 'error': errors.username }"
/>
<span v-if="errors.username">{
{ errors.username }}</span>
<button type="submit" :disabled="isSubmitting">
{
{ isSubmitting ? '提交中...' : '提交' }}
</button>
</form>
</template>
<script setup>
import { useForm } from '@/hooks/useForm'
const {
values,
errors,
isSubmitting,
setFieldValue,
submitForm
} = useForm({
username: '',
email: '',
password: ''
})
const handleSubmit = async () => {
await submitForm(async (formData) => {
// 这里调用API
const response = await api.register(formData)
return response
})
}
</script>
1.2 集成验证功能
基础表单Hook虽然能用,但缺少验证功能。让我们增强它,支持多种验证规则:
// useFormWithValidation.js
import { reactive, computed, toRefs } from 'vue'
// 验证规则定义
const validators = {
required: (value) => !!value || '该字段不能为空',
email: (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(value) || '请输入有效的邮箱地址'
},
minLength: (min) => (value) =>
value.length >= min || `至少需要${min}个字符`,
maxLength: (max) => (value) =>
value.length <= max || `不能超过${max}个字符`,
pattern: (regex, message) => (value) =>
regex.test(value) || message
}
export function useFormWithValidation(initialValues = {}, rules = {}) {
const formState = reactive({
values: { ...initialValues },
errors: {},
touched: {},
isSubmitting: false,
isDirty: false
})
// 计算属性:表单是否有效
const isValid = computed(() => {
return Object.keys(formState.errors).every(key => !formState.errors[key])
})
// 验证单个字段
const validateField = (fieldName) => {
const fieldRules = rules[fieldName]
if (!fieldRules) return true
const value = formState.values[fieldName]
let error = ''
for (const rule of fieldRules) {
const result = typeof rule === 'function'
? rule(value)
: rule(value)
if (typeof result === 'string') {
error = result
break
}
}
formState.errors[fieldName] = error
return !error
}
// 验证整个表单
const validateForm = () => {
let isValid = true
Object.keys(rules).forEach(fieldName => {
const fieldValid = validateField(fieldName)
if (!fieldValid) isValid = false
})
return isValid
}
// 带验证的字段更新
const setFieldValue = (field, value) => {
formState.values[field] = value
formState.touched[field] = true
formState.isDirty = true
// 如果字段已被触摸过,立即验证
if (formState.touched[field]) {
validateField(field)
}
}
// 提交前验证
const submitForm = async (submitFn) => {
// 标记所有字段为已触摸
Object.keys(formState.values).forEach(key => {
formState.touched[key] = true
})
if (!validateForm()) {
throw new Error('表单验证失败')
}
return await submitFn(formState.values)
}
return {
...toRefs(formState),
isValid,
setFieldValue,
validateField,
validateForm,
submitForm
}
}
使用示例:
// 在组件中定义验证规则
const validationRules = {
username: [
validators.required,
validators.minLength(3),
validators.maxLength(20)
],
email: [
validators.required,
validators.email
],
password: [
validators.required,
validators.minLength(8),
validators.pattern(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
'密码必须包含大小写字母和数字'
)
]
}
const form = useFormWithValidation({
username: '',
email: '',
password: ''
}, validationRules)
提示:在实际项目中,你可能会遇到更复杂的验证场景,比如异步验证(检查用户名是否已存在)、跨字段验证(确认密码)等。这些都可以通过扩展验证规则系统来实现。
1.3 性能优化与防抖验证
对于需要实时验证的场景(如检查用户名是否可用),频繁的API调用会导致性能问题。我们可以为验证添加防抖功能:
// useDebouncedValidation.js
import { ref, watch } from 'vue'
import { debounce } from 'lodash-es'
export function useDebouncedValidation(fieldValue, validationFn, delay = 500) {
const error = ref('')
const isValidating = ref(false)
// 防抖验证函数
const debouncedValidate = debounce(async (value) => {
isValidating.value = true
try {
const result = await validationFn(value)
error.value = result || ''
} catch (err) {
error.value = '验证失败'
} finally {
isValidating.value = false
}
}, delay)
// 监听字段值变化
watch(fieldValue, (newValue) => {
if (newValue) {
debouncedValidate(newValue)
} else {
error.value = ''
}
}, { immediate: true })
return {
error,
isValidating
}
}
2. 表格分页与数据请求的完美结合
后台管理系统中最常见的组件就是数据表格,而表格往往伴随着分页、排序、筛选和加载状态管理。让我们创建一个强大的表格Hook来处理这些复杂逻辑。
2.1 基础表格Hook
// useTable.js
import { reactive, toRefs, computed } from 'vue'
export function useTable(fetchApi, options = {}) {
const {
pageSize = 10,
immediate = true,
transformResponse,
transformRequest
} = options
const state = reactive({
data: [],
loading: false,
error: null,
pagination: {
current: 1,
pageSize,
total: 0,
totalPages: 0
},
filters: {},

&spm=1001.2101.3001.5002&articleId=153515297&d=1&t=3&u=a06cc839119340bb87fb1f7f1bf9c84f)
100

被折叠的 条评论
为什么被折叠?



