对于Vue+elementUI的项目而言,表单校验的复用性是一个值得考虑的问题。虽然elementUI默认提供了一些表单校验规则,比如required确定必填项,但是当面对更加复杂多变的实际业务需求时,往往显得不够用,因此就需要自定义校验函数。我们看一下官方案例中自定义校验函数的实现:
export default {
data() {
var checkAge = (rule, value, callback) => {
if (!value) {
return callback(new Error('年龄不能为空'));
}
setTimeout(() => {
if (!Number.isInteger(value)) {
callback(new Error('请输入数字值'));
} else {
if (value < 18) {
callback(new Error('必须年满18岁'));
} else {
callback();
}
}
}, 1000);
};
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.ruleForm.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ruleForm.pass) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
ruleForm: {
pass: '',
checkPass: '',
age: ''
},
rules: {
pass: [
{ validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ validator: validatePass2, trigger: 'blur' }
],
age: [
{ validator: checkAge, trigger: 'blur' }
]
}
};
},
methods: {}
复用性
从以上校验函数中可以看出,校验函数都是放于data中return之外,这仅仅只能应对当前组件的情况。如果其他地方也要使用同样的校验规则,就需要再次写一遍,也就是缺乏复用性
。当然,这些不是官方文档的主题,而是需要我们自己实现。
显然,我们需要将校验规则函数抽取到单独的js文件中区,这样在不同的组件中就可以直接引入了。
耦合性
一旦我们单独抽取之后,就会发现第二个问题:
校验函数中有很强的耦合性。
正如以上案例中比如this.ruleForm.pass
、this.$refs.ruleForm.validateField('checkPass')
,这些都是写死的固定值,假设我们别的表单字段不同,那么校验函数就不能使用了,复用性也就无从谈起。
this指向问题
单独抽取还会带来另一个问题,即
this指向改变,不会指向原先的组件。
这个问题不得到解决,就会导致校验函数的功能大大削弱,不能传递参数。
破局:使用bind吧
事实上,当我们使用bind的时候,耦合性和this指向问题都能得到很好的解决。例如:
// 校验确认密码
export function checkRepeatPwd(rule, value, callback) {
if (value === this) {
callback();
} else {
const error = new Error("两次输入的密码不一致");
callback(error);
}
}
// 校验是否勾选多选框
export function checkCheckbox(rule, value, callback) {
if (value === true) {
callback();
} else {
const error = new Error(this);
callback(error);
}
}
在以上校验规则的定义函数中,this使得传递参数成为可能。现在我们看看在组件中的使用:
import { checkRepeatPwd } from "@/config/validator";
export default {
data() {
return {
formData: {
mobile: "",
authCode: "",
password: "",
repeatPassword: ""
},
formRules: {
mobile: [
{ required: true, message: "请输入手机号码" },
{ validator: checkPhone }
],
authCode: [{ required: true, message: "请输入验证码" }],
password: [{ required: true, message: "请输入密码" }],
repeatPassword: [
{ required: true, message: "请再次输入密码" },
{
validator: checkRepeatPwd.bind(
this.formData && this.formData.password
)
}
]
}
};
}
}
repeatPassword本身和组件耦合度是比较高的,因为需要指定要和哪个字段进行比较,而通过在外面使用bind使得封装的校验规则无需关心具体字段,从而去除了耦合性。
合理设计
- 合理设计bind绑定值。这是因为一方面我们需要通过bind作为中介,将需要的数据传入校验函数中,即实现参数传递的功能,另一方面校验函数可能需要使用组件this能访问到的数据,因此需要设计bind能够进行兼顾。总的说来这并不是大的问题,通过bind几乎可以把调用处的任何数据传入。
以上仅一家之言,当然如果你有更为简洁巧妙的封装方案,欢迎指出!