表单校验是登录注册页的核心功能之一。Element Plus 的 el-form 内置了基于 async-validator 的校验机制,支持必填校验、长度校验、正则匹配、自定义校验函数等多种规则。本节从基础校验规则的定义出发,逐步实现用户名长度校验、密码一致性校验,以及防抖优化的双向联动校验。
校验规则定义
Element Plus 的表单校验通过 rules 属性配置,每个字段对应一组校验规则。首先定义表单接口和校验规则类型:
// types/login.ts
export interface LoginFormInterface {
username: string
password: string
rePassword: string
phone: string | number
code: string
remember: boolean
}
typescript
// components/form/LoginForm.vue
import type { FormRules, FormInstance } from 'element-plus'
import type { LoginFormInterface } from '../../types/login'
const rules = reactive<FormRules<LoginFormInterface>>({
username: [
{
required: true,
message: '用户名不得为空',
trigger: ['blur', 'change'],
},
{
min: 6,
max: 16,
message: '用户名格式不正确',
trigger: ['blur', 'change'],
},
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['blur', 'change'],
},
{
min: 6,
max: 32,
message: '密码长度不正确',
trigger: ['blur', 'change'],
},
],
rePassword: [
{
required: true,
message: '请再次输入密码',
trigger: ['blur', 'change'],
},
{
validator: validateRePassword,
trigger: ['blur', 'change'],
},
],
})
typescript
每个规则对象包含以下关键属性:
| 属性 | 说明 |
|---|---|
required | 是否必填 |
message | 校验失败时的提示信息 |
trigger | 触发校验的时机:blur(失焦)、change(值变化) |
min / max | 字符串长度范围 |
validator | 自定义校验函数 |
将 trigger 同时设为 ['blur', 'change'] 可以确保用户在输入和离开输入框时都会触发校验。
基础校验绑定
在 el-form 上绑定 rules,在每个 el-form-item 上通过 prop 属性指定对应的字段名:
<el-form ref="formRef" :model="form" :rules="rules">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="请输入密码"
show-password
/>
</el-form-item>
<el-form-item prop="rePassword">
<el-input
v-model="form.rePassword"
type="password"
placeholder="请再次输入密码"
show-password
/>
</el-form-item>
</el-form>
vue
自定义校验函数
最典型的自定义校验场景是确认密码与密码的一致性检查。自定义校验函数接收三个参数:rule(规则对象)、value(当前值)、callback(回调函数)。校验通过调用 callback() 无参调用,校验失败则传入 Error 对象:
import type { AsyncValidateRule } from 'async-validator'
// 校验确认密码
const validateRePassword: AsyncValidateRule['validator'] = (
_rule,
value,
callback
) => {
const trimmed = value?.trim?.() ?? value
if (!trimmed) {
callback(new Error('请再次输入密码'))
} else if (trimmed !== form.password) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
}
typescript
密码互校验与防抖
当用户在"确认密码"输入框中输入内容时,需要同时校验"密码"输入框的一致性(因为可能是先修改了确认密码,再回来修改密码)。反之亦然。但如果两个输入框互相触发对方的校验,就会形成无限递归,导致堆栈溢出。
解决方法是使用 useDebounceFn 为校验函数添加延迟执行:
import { useDebounceFn } from '@vueuse/core'
// 校验确认密码(防抖)
const validateRePassword: AsyncValidateRule['validator'] = (
_rule,
value,
callback
) => {
const trimmed = value?.trim?.() ?? value
if (!trimmed) {
callback(new Error('请再次输入密码'))
} else if (trimmed !== form.password) {
callback(new Error('两次输入的密码不一致'))
} else {
// 密码一致时,触发密码框的重新校验
debouncedValidatePassword()
callback()
}
}
// 防抖校验密码框
const debouncedValidatePassword = useDebounceFn(() => {
formRef.value?.validateField('password')
}, 200)
// 校验密码(防抖) - 密码框变化时也校验确认密码框
const validatePassword: AsyncValidateRule['validator'] = (
_rule,
value,
callback
) => {
const trimmed = value?.trim?.() ?? value
if (!trimmed) {
callback(new Error('请输入密码'))
} else {
// 密码变化时,触发确认密码框的重新校验
debouncedValidateRePassword()
callback()
}
}
const debouncedValidateRePassword = useDebounceFn(() => {
formRef.value?.validateField('rePassword')
}, 200)
typescript
将校验函数的类型改为 async-validator 的异步校验类型,避免类型错误:
const rules = reactive<FormRules<LoginFormInterface>>({
password: [
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] },
{ min: 6, max: 32, message: '密码长度不正确', trigger: ['blur', 'change'] },
{ validator: validatePassword, trigger: ['blur', 'change'] },
],
rePassword: [
{ required: true, message: '请再次输入密码', trigger: ['blur', 'change'] },
{ validator: validateRePassword, trigger: ['blur', 'change'] },
],
})
typescript
提交时整表校验
用户点击登录按钮时,需要对整个表单进行完整校验。通过 formRef 获取表单实例,调用其 validate 方法:
const formRef = ref<FormInstance>()
const handleSubmit = async () => {
if (!formRef.value) return
try {
const valid = await formRef.value.validate()
if (valid) {
emit('submit', { ...form })
}
} catch {
// 校验失败,Element Plus 会自动显示错误信息
}
}
typescript
validate() 返回一个 Promise,校验通过时 resolve 为 true,校验失败时 reject。这样确保只有在所有字段都校验通过的情况下才会提交表单数据。
重置表单
如果需要重置表单校验状态和值,可以使用 resetFields 方法:
const handleReset = () => {
formRef.value?.resetFields()
}
typescript
输入内容的 trim 处理
在自定义校验函数中对输入值进行 trim() 处理,防止用户输入纯空格通过校验:
const trimmed = value?.trim?.() ?? value
if (!trimmed) {
callback(new Error('用户名不得为空'))
}
typescript
同时为密码输入框设置 type="password" 使输入内容以掩码形式显示,避免明文暴露。
async-validator 支持的校验属性
Element Plus 的表单校验底层使用 async-validator 库,除了 required、min、max、validator 外,还支持以下常用属性:
| 属性 | 类型 | 说明 |
|---|---|---|
pattern | RegExp | 正则表达式匹配 |
type | string | 值类型校验(email、url、number 等) |
len | number | 精确长度 |
enum | string[] | 枚举值校验 |
whitespace | boolean | 是否允许纯空格 |
例如邮箱校验:
{
type: 'email',
message: '请输入正确的邮箱格式',
trigger: ['blur', 'change'],
}
typescript
总结
Element Plus 表单校验的关键流程:定义 rules -> 绑定到 el-form -> 为每个 el-form-item 指定 prop -> 提交时调用 validate() 整表校验。自定义校验函数通过 callback 机制返回校验结果。密码一致性校验是最常见的自定义场景,需要特别注意互校验时的防抖处理,使用 useDebounceFn 避免堆栈溢出。
↑