JS常见的API扩展形式(prototype、jquery、vue插件封装)以及怎样设计出易扩展的表单验证功能?

  • A+
所属分类:Web前端
摘要

比如我现在有一个需求,给定一个字符串,给方法传递一个参数为数字类型来确定当前字符串重复次数,例如:


常见的API扩展形式

prototype

比如我现在有一个需求,给定一个字符串,给方法传递一个参数为数字类型来确定当前字符串重复次数,例如:

'abc'.repeatStringNumTimes(3) // abcabcabc

如果按照一般的思维就是我们把这个方法绑定到String的原型上,如下代码:

String.prototype.repeatStringNumTimes = String.prototype.repeatStringNumTimes || function(times) {     var str = '';     for(var i = 0; i < times; i++) {         str += this;     }     return str; }

 jQuery

根据《jQuery高级编程》的描述,jQuery插件开发方式主要有三种:

  • 通过$.extend()来扩展jQuery

  • 通过$.fn 向jQuery添加新的方法

  • 通过$.widget()应用jQuery UI的部件工厂方式创建

通常我们使用第二种方法来进行简单插件开发,说简单是相对于第三种方式。第三种方式是用来开发更高级jQuery部件的,该模式开发出来的部件带有很多jQuery内建的特性,比如插件的状态信息自动保存,各种关于插件的常用方法等,非常贴心,这里不细说。

而第一种方式又太简单,仅仅是在jQuery命名空间或者理解成jQuery身上添加了一个静态方法而以。所以我们调用通过.extend().extend()添加的函数时直接通过符号调用($.myfunction())而不需要选中DOM元素($('#example').myfunction())。请看下面的例子。

$.extend({     sayHello: function(name) {         console.log('Hello,' + (name ? name : 'Dude') + '!');     } }) $.sayHello(); //调用 $.sayHello('Wayou'); //带参调用

看一个jquery封装的面向对象的插件开发代码:

//定义Beautifier的构造函数 var Beautifier = function(ele, opt) {     this.$element = ele,     this.defaults = {         'color': 'red',         'fontSize': '12px',         'textDecoration':'none'     },     this.options = $.extend({}, this.defaults, opt) } //定义Beautifier的方法 Beautifier.prototype = {     beautify: function() {         return this.$element.css({             'color': this.options.color,             'fontSize': this.options.fontSize,             'textDecoration': this.options.textDecoration         });     } } //在插件中使用Beautifier对象 $.fn.myPlugin = function(options) {     //创建Beautifier的实体     var beautifier = new Beautifier(this, options);     //调用其方法     return beautifier.beautify(); }

调用方式:

$(function() {     $('a').myPlugin({         'color': '#2C9929',         'fontSize': '20px'     }); })

 感兴趣的可以详细查看文章:《jQuery插件开发进阶》

 vue

插件通常会为 Vue 添加全局功能。插件的范围没有限制——一般有下面几种:

  • 1.添加全局方法或者属性,如: vue-custom-element
  • 2.添加全局资源:指令/过滤器/过渡等,如 vue-touch
  • 3.通过全局 mixin 方法添加一些组件选项,如: vue-router
  • 4.添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  • 5.一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 vue-router

Vue.js 的插件应当有一个公开方法 install 。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:

MyPlugin.install = function (Vue, options) {   // 1. 添加全局方法或属性   Vue.myGlobalMethod = function () {     // 逻辑...   }    // 2. 添加全局资源   Vue.directive('my-directive', {     bind (el, binding, vnode, oldVnode) {       // 逻辑...     }     ...   })    // 3. 注入组件   Vue.mixin({     created: function () {       // 逻辑...     }     ...   })    // 4. 添加实例方法   Vue.prototype.$myMethod = function (methodOptions) {     // 逻辑...   } } export default MyPlugin

封装的插件怎样使用?通过全局方法 Vue.use() 使用插件:

// 调用 `MyPlugin.install(Vue)` Vue.use(MyPlugin)

 也可以传入一个选项对象:

Vue.use(MyPlugin, { someOption: true })

 实现一个表单验证

设计表单验证的规则就是开放-封闭验证,使用策略模式封装。

一般方案

我们先写一个html代码片段好验证代码:

<form action="" id="registerForm" method="post" onsubmit="return submitValidate()"> <p>     <label>请输入用户名:</label>     <input type="text" name="userName" /> </p> <p>     <label>请输入密码:</label>     <input type="text" name="password" /> </p> <p>     <label>请输入手机号码:</label>     <input type="text" name="phoneNumber" /> </p> <div>     <button type="submit">提交</button> </div> </form>

在form上绑定的submit,想实现的submitValidate方法代码如下:

function submitValidate() {     var registerForm = document.getElementById("registerForm");     var rulesArr = [         {             el: registerForm.userName.value,             rules: [{rule: 'isNonEmpty',message: '用户名不能为空'},{rule:'minLength:3',message: '用户名长度不能小于3位'}]         },         {             el: registerForm.password.value,             rules: [{rule: 'isNonEmpty',message: '密码不能为空'},{rule:'minLength:6',message: '密码的长度不能小于6位'}]         },         {             el: registerForm.phoneNumber.value,             rules: [{rule: 'isNonEmpty',message: '手机号不能为空'},{rule:'isMobile',message: '手机号码格式不正确'}]         }     ]     var resultMsg = validate.check(rulesArr);     if(resultMsg) {         alert(resultMsg);         return false;     }     return true; }

下面我们编写validate验证方法,代码如下:

var validate = (function() {     // 校验规则的各种算法     var rules = {         // 判断非空         isNonEmpty: function(value,errorMsg) {             if(!value) {                 return errorMsg;             }         },         // 判断最小长度         minLength: function(value,length,errorMsg) {             if(value.toString().length < length) {                 return errorMsg;             }         },         // 判断最大长度         maxLength: function(value,length,errorMsg) {             if(value.toString().length > length) {                 return errorMsg;             }         },         // 判断手机号         isMobile: function(value,errorMsg) {             if (!/(^1[0-9]{10}$)/.test(value)) {                 return errorMsg;             }         },         // 判断座机电话         isTel: function(value,errorMsg) {             if(!/^d{3}-d{8}|d{4}-d{7}|d{11}$/.test(value)) {                 return errorMsg;             }         }     }               return {         /** 校验方法          * @param {Array} arr          * @return {*}          * */         check: function(arr) {             var ruleMsg;             var checkRule;             var _rule;             for(var i = 0, len = arr.length; i < len; i++) {                 // 没有当前校验字段                 if(arr[i].el === undefined) {                     return '没有当前字段'                 }                 for(var j = 0, ruleLen = arr[i].rules.length; j < ruleLen; j++) {                     var ruleObj = arr[i].rules[j];                     checkRule = ruleObj.rule.split(':'); // rule规则存在minLenth:6这样的校验                     _rule = checkRule.shift(); // 获取校验算法名称                     checkRule.unshift(arr[i].el); // checkRule首位存入校验的value值                     checkRule.push(ruleObj.message); // checkRule末尾加入校验的message                     ruleMsg = rules[_rule].apply(null,checkRule);                     if(ruleMsg) {                         return  ruleMsg;                     }                 }             }         }     }     })();

以上代码就是常规实现的表单验证,看似也够用了,但是如果一个系统中有多个表单提交验证,并且有部分验证方式不是那么通用,我们不可能再去修改代码中的rules,那样这个rules校验的算法越来越多,并且很多不是很通用的,那么我们怎样来解决呢?

在验证函数中增加一个添加规则算法的方法,代码如下:

var validate = (function() {     // 校验规则的各种算法     var rules = {         // 判断非空         isNonEmpty: function(value,errorMsg) {             if(!value) {                 return errorMsg;             }         },         // 判断最小长度         minLength: function(value,length,errorMsg) {             if(value.toString().length < length) {                 return errorMsg;             }         },         // 判断最大长度         maxLength: function(value,length,errorMsg) {             if(value.toString().length > length) {                 return errorMsg;             }         },         // 判断手机号         isMobile: function(value,errorMsg) {             if (!/(^1[0-9]{10}$)/.test(value)) {                 return errorMsg;             }         },         // 判断座机电话         isTel: function(value,errorMsg) {             if(!/^d{3}-d{8}|d{4}-d{7}|d{11}$/.test(value)) {                 return errorMsg;             }         }     }               return {         /** 校验方法          * @param {Array} arr          * @return {*}          * */         check: function(arr) {             var ruleMsg;             var checkRule;             var _rule;             for(var i = 0, len = arr.length; i < len; i++) {                 // 没有当前校验字段                 if(arr[i].el === undefined) {                     return '没有当前字段'                 }                 for(var j = 0, ruleLen = arr[i].rules.length; j < ruleLen; j++) {                     var ruleObj = arr[i].rules[j];                     checkRule = ruleObj.rule.split(':'); // rule规则存在minLenth:6这样的校验                     _rule = checkRule.shift(); // 获取校验算法名称                     checkRule.unshift(arr[i].el); // checkRule首位存入校验的value值                     checkRule.push(ruleObj.message); // checkRule末尾加入校验的message                     ruleMsg = rules[_rule].apply(null,checkRule);                     if(ruleMsg) {                         return  ruleMsg;                     }                 }             }         },                  // 添加规则         addRule: function(ruleName,fn) {             rules[ruleName] = fn;         }     }     })();

比如用户名只能是字母跟数字的组合,那么我们就添加一个规则,代码如下:

validate.addRule('isAlphaNum', function(value, errorMsg) {     if (/[^a-zA-Z0-9]/.test(value)) {         return errorMsg;     } })

submitValidate方法的代码修改为如下:

function submitValidate() {     var registerForm = document.getElementById("registerForm");     validate.addRule('isAlphaNum', function(value, errorMsg) {         if (/[^a-zA-Z0-9]/.test(value)) {             return errorMsg;         }     })     var rulesArr = [{             el: registerForm.userName.value,             rules: [{                 rule: 'isNonEmpty',                 message: '用户名不能为空'             }, {                 rule: 'minLength:3',                 message: '用户名长度不能小于3位'             }, {                 rule: 'isAlphaNum',                 message: '用户名只能是数字跟字母的组合'             }]         },         {             el: registerForm.password.value,             rules: [{                 rule: 'isNonEmpty',                 message: '密码不能为空'             }, {                 rule: 'minLength:6',                 message: '密码的长度不能小于6位'             }]         },         {             el: registerForm.phoneNumber.value,             rules: [{                 rule: 'isNonEmpty',                 message: '手机号不能为空'             }, {                 rule: 'isMobile',                 message: '手机号码格式不正确'             }]         }     ]     var resultMsg = validate.check(rulesArr);     if (resultMsg) {         alert(resultMsg);         return false;     }     return true; }

 运行效果如图所示:

JS常见的API扩展形式(prototype、jquery、vue插件封装)以及怎样设计出易扩展的表单验证功能?

升级方案

 如果我们想实现如图这样的验证结果:

JS常见的API扩展形式(prototype、jquery、vue插件封装)以及怎样设计出易扩展的表单验证功能?

那么我们就需要保存所有元素的错误信息,那么我们新添加一个checkAll的方法,代码如下:

var validate = (function() {     // 校验规则的各种算法     var rules = {         // 判断非空         isNonEmpty: function(value, errorMsg) {             if (!value) {                 return errorMsg;             }         },         // 判断最小长度         minLength: function(value, length, errorMsg) {             if (value.toString().length < length) {                 return errorMsg;             }         },         // 判断最大长度         maxLength: function(value, length, errorMsg) {             if (value.toString().length > length) {                 return errorMsg;             }         },         // 判断手机号         isMobile: function(value, errorMsg) {             if (!/(^1[0-9]{10}$)/.test(value)) {                 return errorMsg;             }         },         // 判断座机电话         isTel: function(value, errorMsg) {             if (!/^d{3}-d{8}|d{4}-d{7}|d{11}$/.test(value)) {                 return errorMsg;             }         }     }       return {         /** 校验方法          * @param {Array} arr          * @return {*}          * */         check: function(arr) {             var ruleMsg;             var checkRule;             var _rule;             for (var i = 0, len = arr.length; i < len; i++) {                 // 没有当前校验字段                 if (arr[i].el === undefined) {                     return '没有当前字段'                 }                 for (var j = 0, ruleLen = arr[i].rules.length; j < ruleLen; j++) {                     var ruleObj = arr[i].rules[j];                     checkRule = ruleObj.rule.split(':'); // rule规则存在minLenth:6这样的校验                     _rule = checkRule.shift(); // 获取校验算法名称                     checkRule.unshift(arr[i].el); // checkRule首位存入校验的value值                     checkRule.push(ruleObj.message); // checkRule末尾加入校验的message                     ruleMsg = rules[_rule].apply(null, checkRule);                     if (ruleMsg) {                         return ruleMsg;                     }                 }             }         },                  // 校验所有接口         checkAll: function(arr) {             var ruleMsg;             var checkRule;             var _rule;             var reusltMsg = [];             for (var i = 0, len = arr.length; i < len; i++) {                 // 没有当前校验字段                 if (arr[i].el === undefined) {                     return '没有当前字段'                 }                 for (var j = 0, ruleLen = arr[i].rules.length; j < ruleLen; j++) {                     var ruleObj = arr[i].rules[j];                     checkRule = ruleObj.rule.split(':'); // rule规则存在minLenth:6这样的校验                     _rule = checkRule.shift(); // 获取校验算法名称                     checkRule.unshift(arr[i].el); // checkRule首位存入校验的value值                     checkRule.push(ruleObj.message); // checkRule末尾加入校验的message                     ruleMsg = rules[_rule].apply(null, checkRule);                     if (ruleMsg) {                         reusltMsg.push({                             el: arr[i].el,                             rules: _rule,                             message: ruleMsg,                             alias: arr[i].alias   // 绑定一个别名用处:绑定到具体的一个DOM元素上显示错误信息                         })                         break; // 跳出当前循环,不用把当前一个元素上多个验证不通过结果都存储起来                     }                 }             }             return reusltMsg.length > 0 ? reusltMsg : false;         },          // 添加规则         addRule: function(ruleName, fn) {             rules[ruleName] = fn;         }     } })();

我们调整下html代码:

<form action="" id="registerForm" method="post" onsubmit="return submitValidate()">     <p>         <label>请输入用户名:</label>         <input type="text" name="userName" />         <span class="error"></span>     </p>     <p>         <label>请输入密码:</label>         <input type="text" name="password" />         <span class="error"></span>     </p>     <p>         <label>请输入手机号码:</label>         <input type="text" name="phoneNumber" />         <span class="error"></span>     </p>     <div>         <button type="submit">提交</button>     </div> </form>

css样式:

<style>     .error {         color: red;     } </style>

submitValidate方法的代码调整为:

function submitValidate() {     var registerForm = document.getElementById("registerForm");     validate.addRule('isAlphaNum', function(value, errorMsg) {         if (/[^a-zA-Z0-9]/.test(value)) {             return errorMsg;         }     })     var rulesArr = [{             el: registerForm.userName.value,             alias: 'userName',             rules: [{                 rule: 'isNonEmpty',                 message: '用户名不能为空'             }, {                 rule: 'minLength:3',                 message: '用户名长度不能小于3位'             }, {                 rule: 'isAlphaNum',                 message: '用户名只能是数字跟字母的组合'             }]         },         {             el: registerForm.password.value,             alias: 'password',             rules: [{                 rule: 'isNonEmpty',                 message: '密码不能为空'             }, {                 rule: 'minLength:6',                 message: '密码的长度不能小于6位'             }]         },         {             el: registerForm.phoneNumber.value,             alias: 'phoneNumber',             rules: [{                 rule: 'isNonEmpty',                 message: '手机号不能为空'             }, {                 rule: 'isMobile',                 message: '手机号码格式不正确'             }]         }     ]     var resultMsg = validate.checkAll(rulesArr);     if (resultMsg) {         for(var re = 0, len = resultMsg.length; re < len; re++) {             var curResult = resultMsg[re];             var errorDom = document.querySelector('#registerForm p [name="'+curResult.alias+'"]').nextElementSibling;             errorDom.innerHTML = curResult.message;         }                  return false;     }     return true; }

 这样得到的结果就是我们刚才截图的结果了。

 参考地址