优雅的写表单校验 -- 策略模式的运用
假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑。
- 用户名不能为空。
- 密码长度不能少于 6 位。
- 手机号码必须符合格式。
<form action="http:// xxx.com/register" id="registerForm" method="post"> <div>请输入用户名:<input type="text" name="userName" /></div> <div>请输入密码:<input type="text" name="password" /></div> <div>请输入手机号码:<input type="text" name="phoneNumber" /></div> <button>提交</button> </form>
html 的部分很简单,主要想想提交里怎么写?
来,简单构思下~
来,简单构思下~
来,简单构思下~
凭直觉写的表单校验 - v1
let form = document.querySelector("#registerForm"); form.onsubmit = () => { if (form.userName.value === "") { alert("用户名不能为空"); return false; } if (form.password.value.length < 6) { alert("密码不能少于6位"); return false; } if (!/[0-9]{11,11}/.test(form.phoneNumber.value)) { alert("手机号码格式不正确"); return false; } };
一顿操作猛如虎,好,写完之后,看看代码有何不妥:
- registerForm.onsubmit 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有 的校验规则。
- registerForm.onsubmit 函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长 度校验从 6 改成 8,我们都必须深入 registerForm.onsubmit 函数的内部实现,这是违反开 放—封闭原则的。
- 算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的 校验,那我们很可能将这些校验逻辑复制得漫天遍野。
联系之前说过的策略模式,来改进代码~
改进版的表单校验 - v2
// 将算法的实现单独封装起来 var strategies = { isNonEmpty: function(value, errorMsg) { // 不为空 if (value === "") { return errorMsg; } }, // 这里把length作为参数 minLength: function(value, length, errorMsg) { // 限制最小长度 if (value.length < length) { return errorMsg; } }, isMobile: function(value, errorMsg) { // 手机号码格式 if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) { return errorMsg; } } }; // 算法的使用 let form = document.querySelector("#registerForm"); form.onsubmit = function() { var errorMsg = "用户名不能为空"; if ((strategies.isNonEmpty(registerForm.userName.value), errorMsg)) { alert(errorMsg); return false; } var errorMsg = "密码长度不能少于6 位"; if ((strategies.minLength(registerForm.password.value), 6, errorMsg)) { alert(errorMsg); return false; } var errorMsg = "手机号码格式不正确"; if ((strategies.isMobile(registerForm.phoneNumber.value), errorMsg)) { alert(errorMsg); return false; } // 这里开始ajax请求。。。 };
写完之后,我屮艸芔茻!!!这好像和第一版也没什么大的区别。看起来一点也不优雅!!!
但这里提供了关键性思路,提交部分明显就是重复,很容易想到列表循环,这里将其抽象成 validator 的类。
也就是 validator 类提供 add 和 start,,add 负责将每种校验方式放进一个数组,start 负责遍历数组,一旦有返回值直接返回且终止返回。
。。。咳咳,有点难度,可以试试写写。
初见美好 - v3
// var strategies = 、、、 class Validator { constructor() { this.cache = []; } add(dom, rule, msg) { // 把单个验证表单的fn存下来 let [strategy, length] = rule.split(":"); let params = length ? [dom.value, length, msg] : [dom.value, msg]; console.log(params); let fn = () => { return strategies[strategy](...params); }; this.cache.push(fn); } start() { for (var i = 0; i < this.cache.length; i++) { let errMsg = this.cache[i](); if (errMsg) { return errMsg; } } } } // 应用 let form = document.querySelector("#registerForm"); function validateData() { let validator = new Validator(); validator.add(form.userName, "isNonEmpty", "用户名不能为空"); validator.add(form.password, "minLength:6", "密码不能少于6位"); validator.add(form.phoneNumber, "isMobile", "手机号码格式不正确"); // 需要加新的校验规则,直接添加这里就好 let errMsg = validator.start(); return errMsg; } // submit里面就很优雅了 form.onsubmit = () => { let errMsg = validateData(); if (errMsg) { alert(errMsg); return false; } };
写完这些之后,很赞啦!
但。。。还有点点缺点,比如用户名想同时验证其为空和长度的话,需要单个列出,显然又累赘了
接下来试着将其改成validator.add(form.userName, {isNonEmpty:'用户名不能为空','minLength:6':'用户名长度不能少于6位'})
validator.add(form.userName, "isNonEmpty", "用户名不能为空"); validator.add(form.userName, "minLength:6", "用户名长度不能少于6位");
相知相识 - v4
// var strategies = 。。。; class Validator { constructor() { this.cache = []; } // 没事原先的add提取出来 _handleRule(dom, rule, msg) { // 把单个验证表单的fn存下来 let [strategy, length] = rule.split(":"); let params = length ? [dom.value, length, msg] : [dom.value, msg]; console.log(params); let fn = () => { return strategies[strategy](...params); }; this.cache.push(fn); } add(dom, rules) { rules.forEach(item => { for (let rule in item) { let msg = item[rule]; // 遍历的时候,直接使用就好 this._handleRule(dom, rule, msg); } }); } start() { for (var i = 0; i < this.cache.length; i++) { let errMsg = this.cache[i](); if (errMsg) { return errMsg; } } } } // 应用 let form = document.querySelector("#registerForm"); function validateData() { let validator = new Validator(); validator.add(form.userName, [ { isNonEmpty: "用户名不能为空" }, { "minLength:6": "用户名不能少于6位" } ]); validator.add(form.password, [{ "minLength:6": "密码不能少于6位" }]); validator.add(form.phoneNumber, [{ isMobile: "手机号码格式不正确" }]); console.log(validator); let errMsg = validator.start(); return errMsg; } form.onsubmit = () => { let errMsg = validateData(); if (errMsg) { alert(errMsg); return false; } };
完结,下次看表单验证的一些插件可能就能加深理解了!
引用
这里的案例是《JavaScript的设计模式与开发实践》里面的,写的非常好,强烈安利!!!
对了之前写了个策略模式的入门