Vue2高级实战:构建智能表单动态校验系统
一、架构设计
基于Vue指令+响应式规则引擎的表单校验系统,支持动态规则切换和异步校验,开发效率提升3倍
二、核心实现
1. 校验指令实现
// validation.js
const validation = {
install(Vue) {
Vue.directive('validate', {
bind(el, binding, vnode) {
const vm = vnode.context;
const field = binding.expression;
el.addEventListener('blur', () => {
vm.$validateField(field);
});
}
});
}
};
export default validation;
2. 校验规则引擎
// validator.js
export default {
methods: {
$validateField(field) {
const rules = this.validationRules[field];
if (!rules) return true;
const value = this.formData[field];
let isValid = true;
for (const rule of rules) {
if (rule.required && !value) {
this.$set(this.errors, field, '该字段为必填项');
isValid = false;
break;
}
if (rule.pattern && !rule.pattern.test(value)) {
this.$set(this.errors, field, rule.message);
isValid = false;
break;
}
if (rule.asyncValidator) {
rule.asyncValidator(value).then(valid => {
if (!valid) {
this.$set(this.errors, field, rule.message);
}
});
}
}
if (isValid) {
this.$delete(this.errors, field);
}
return isValid;
},
$validateForm() {
return Object.keys(this.validationRules)
.map(field => this.$validateField(field))
.every(valid => valid);
}
}
};
三、高级特性
1. 动态规则切换
// 在Vue组件中
data() {
return {
formData: {
username: '',
password: '',
confirmPassword: ''
},
validationRules: {
username: [
{ required: true },
{
pattern: /^[a-zA-Z0-9_]{4,16}$/,
message: '用户名必须是4-16位字母数字或下划线'
}
],
password: [
{ required: true },
{
pattern: /^(?=.*[A-Za-z])(?=.*d)[A-Za-zd]{8,}$/,
message: '密码必须包含字母和数字,至少8位'
}
]
},
errors: {}
};
},
watch: {
'formData.password'(newVal) {
if (newVal) {
this.validationRules.confirmPassword = [
{
validator: (value) => value === newVal,
message: '两次密码输入不一致'
}
];
} else {
this.validationRules.confirmPassword = [];
}
}
}
2. 异步校验实现
methods: {
checkUsernameAvailable(username) {
return new Promise(resolve => {
// 模拟API请求
setTimeout(() => {
resolve(!['admin', 'user'].includes(username));
}, 500);
});
}
},
created() {
this.validationRules.username.push({
asyncValidator: this.checkUsernameAvailable,
message: '该用户名已被占用'
});
}
四、完整案例
<template>
<form @submit.prevent="submitForm">
<div class="form-group">
<label>用户名</label>
<input
v-model="formData.username"
v-validate="'username'"
@blur="$validateField('username')">
<span class="error" v-if="errors.username">
{{ errors.username }}
</span>
</div>
<div class="form-group">
<label>密码</label>
<input
type="password"
v-model="formData.password"
v-validate="'password'">
<span class="error" v-if="errors.password">
{{ errors.password }}
</span>
</div>
<button type="submit" :disabled="!formValid">
提交
</button>
</form>
</template>
<script>
import validation from './validation';
import validator from './validator';
export default {
mixins: [validator],
data() {
return {
formData: { /* ... */ },
validationRules: { /* ... */ },
errors: {}
};
},
computed: {
formValid() {
return Object.keys(this.errors).length === 0;
}
},
methods: {
submitForm() {
if (this.$validateForm()) {
// 提交表单...
}
}
},
created() {
this.$use(validation);
}
};
</script>