對於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幾乎可以把調用處的任何數據傳入。
以上僅一家之言,當然如果你有更爲簡潔巧妙的封裝方案,歡迎指出!