数据校验插件开发

在看完了《JavaScript插件编写指南》之后,最激动人心的时刻到了!我们着手开始做一个数据校验插件吧!

首先,我们先初始化一个HTML用来作为校验的数据来源

<body>
    <div>
        <form>
            <div>
                <label for="exampleInputEmail1">邮箱</label>
                <input type="email" id="exampleInputEmail1" placeholder="请输入合法邮箱">
            </div>
            <div>
                <label for="exampleInputPassword1">手机号码</label>
                <input type="text" id="exampleInputPhone1" placeholder="请输入合法手机号码">
            </div>
            <div>
                <label for="exampleInputPassword1">密码</label>
                <input type="password" id="exampleInputPassword1" placeholder="请输入有效密码">
            </div>
            <div>
                <label for="exampleInputPassword2">确认密码</label>
                <input type="password" id="exampleInputPassword2" placeholder="请再次输入密码">
            </div>

            <button type="submit">注册</button>
        </form>
    </div>
    <script src="jquery2.2.4.js"></script>
    <script src="dataValidate.js"></script>
</body>

首先初始化插件

(function(root, factory, plug){
    // 工厂函数,用来立即执行下面的function,同时传入jquery对象和插件名称
    factory($,plug)
    // this在非严格模式的全局下代表window对象
})(this, function($,plug){
    // 测试是否成功调用了工厂函数
    console.log(plug);
},"dataValidate")

因为我们是依赖jQuery而开发的插件,所以我们需要把我们的插件绑定在jQuery实对象上,即绑定到jQuery的原型上去。

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    // 在jQuery中扩展dataValidate方法
    $.prototype[plug] = function() {

    }
}, "dataValidate")

我们可以在HTML上测试jQuery中是否成功绑定了这个属性

<script>
    console.log($().dataValidate)
</script>

由于我们的插件只是针对form表单中的input进行校验,并不会对所有的input的值进行校验,所以我们接下来需要做一个限制:

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    $.prototype[plug] = function() {
        // this 代表的是jquery的实对象
        // 如果不在form中,则直接return出去,不进行校验
        if (!this.is("form")) {
            return;
        }
    }
}, "dataValidate")

接下来,我们知道,我们需要对input进行校验,所以要找到form下面的所有input,并把它绑定到jQuery的一个属性上:

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    $.prototype[plug] = function() {
        if (!this.is("form")) {
            return;
        }
        // 将所有input绑定到jquery的$file中
        this.$file = this.find("input");
        // 对所有的input绑定一个input事件
        this.$file.on("input", function() {
            // 进行测试
            // 在事件函数里面,this代表触发这个事件的element对象(即这个this)
            // 在构造函数里,this代表的是这个构造函数的实对象
            console.log(this.value)
        })
    }
}, "dataValidate")

但是这个时候,我有一个问题,这个input事件肯定不能满足所有人的需求,因为这个插件是为了团队和用户开发的,用户可能需要一个blur事件来进行触发,这个时候该怎么做?

解疑======
这个input事件肯定不能写死,这个时候我们就需要用到默认配置,将不确定的因素利用默认配置代替。

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    // 存储默认配置
    var DAFAULT = {
        initEvent: "input"
    }
    $.prototype[plug] = function() {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        // 但是问题又来了,难道就这么写DAFAULT.initEvent吗?
        this.$file.on(DAFAULT.initEvent, function() {})
    }
}, "dataValidate")

其实我们也可以使用另外一种方式,首先我会想个办法,我会把我默认配置里面的这些参数和属性全部扩展到另外一个对象上面去------this,因为this代表了jQuery的实对象,我会把这默认配置都扩展到jQuery的实对象上,谁使用我,我就把这些属性扩展到谁身上去。

那么,怎么进行扩展呢?
其实jQuery已经给我们准备好了,这个时候就用到了$.extend()这个方法,这个东西实现其实很简单,以后有空解读实现过程,现在你们知道他的用法就行了。

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    var DAFAULT = {
        initEvent: "input"
    }
    $.prototype[plug] = function() {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        $.extend(this, DAFAULT);
        // 进行测试
        console.log(this.initEvent) // input
        this.$file.on(this.initEvent, function() {})
    }
}, "dataValidate")

使用了默认配置,但是还有一个问题,用户怎么使用他自己的配置呢?

当然是用户要用什么,就自己传什么。

<script>
    $("form").dataValidate({
        // 用户怎么知道initEvent呢?
        // 那就是通过插件的接口文档来告诉用户,这个字段是配置触发事件的
        initEvent: "blur"
    })
</script>
(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    var DAFAULT = {
        initEvent: "input"
    }
    // 接收传过来的用户配置参数
    $.prototype[plug] = function(options) {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        // 直接进行扩展
        $.extend(this, DAFAULT, options);
        // 进行测试
        console.log(this.initEvent) // input
        this.$file.on(this.initEvent, function() {})
    }
}, "dataValidate")

在这个问题解决之后,我们要开始思考一系列的问题,例如,我们需要给这个插件提供基础的默认规则。

数据校验插件我们需要提供以下基础的一些功能:

  1. 正则(reg)
  2. 输入不能为空(required)
  3. 长度最小值(min-length)
  4. 文本一致(confirm)
  5. 提示 (-message)
  6. 扩展用户自定义配置

明白了这些基础功能之后,我们就在HTML里将规则先补上,

我们可以通过配置自定义属性(data-)(也叫做指令)的方式来进行配置:

但是我们要清楚哪些是我们的配置,哪些是这个标签扩展的自定义属性,如果都是以data开头,那就无法区分是配置还是扩展的自定义属性,所以这个时候要加个标签来表明这是我的插件配置dv,所以在接口文档里面,也要告诉用户通过data-dv来进行配置

<form>
    <div>
        <label for="exampleInputEmail1">邮箱</label>
        <!--
                  不能为空
                  邮箱正则
               -->
        <input type="email" id="exampleInputEmail1" placeholder="请输入合法邮箱" data-dv-required=true data-dv-required-message="邮箱不能为空" data-dv-reg="^\w+@\w+\.\w+$" data-dv-reg-message="邮箱格式不正确">
    </div>
    <div>
        <label for="exampleInputPassword1">手机号码</label>
        <!--
                  不能为空
                  手机号正则
               -->
        <input type="text" id="exampleInputPassword1" placeholder="请输入合法手机号码" data-dv-required=true data-dv-required-message="手机号码不能为空" data-dv-reg="^1\d{10}$" data-dv-reg-message="手机号码格式不正确">
    </div>
    <div>
        <label for="exampleInputPassword1">密码</label>
        <!--
                  不能为空
                  密码正则
                  字符长度
               -->
        <input type="password" id="exampleInputPassword1" placeholder="请输入有效密码" data-dv-required=true data-dv-required-message="密码不能为空" data-dv-reg="^\w+$" data-dv-reg-message="密码格式不正确" data-dv-min-length=6 data-dv-min-length-message="密码长度不正确">
    </div>
    <div>
        <label for="exampleInputPassword1">确认密码</label>
        <!--
                  不能为空
                  密码一致
               -->
        <input type="password" id="exampleInputPassword1" placeholder="请再次输入密码" data-dv-required=true data-dv-required-message="密码不能为空" data-dv-confirm=true data-dv-confirm-message="两次密码不一致">
    </div>
</form>

配置完了之后,你会发现,我们配置的这些玩意好像并没有什么用,所以下面我们就需要根据页面上的配置在js里面来写相应的默认规则。

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    var DAFAULT = {
        initEvent: "input"
    }
    // 默认规则
    var RULES = {
        "reg": function() {},
        "required": function() {},
        "min-length": function() {},
        "confirm": function() {}
    }
    // 接收传过来的用户配置参数
    $.prototype[plug] = function(options) {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        $.extend(this, DAFAULT, options);
        this.$file.on(this.initEvent, function() {
            // 遍历默认配置
            $.each(RULES, function(key, fn) {
                // 这个时候this指的是element对象
                // 我们想用this.data来获取data上面用户自定义的属性,但是这个方法只有jQuery对象才拥有,这个时候该怎么办?
            });
        })
    }
}, "dataValidate")

其实很简单,包装一下就可以了,将element对象包装成jQuery对象。

(function(root, factory, plug) {
    factory($, plug)
})(this, function($, plug) {
    var DAFAULT = {
        initEvent: "input",
        plugName: "dv"
    }
    // 默认规则
    var RULES = {
        "reg": function() {},
        "required": function() {},
        "min-length": function() {},
        "confirm": function() {}
    }
    // 接收传过来的用户配置参数
    $.prototype[plug] = function(options) {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        $.extend(this, DAFAULT, options);
        this.$file.on(this.initEvent, function() {
            // 包装
            var _this = $(this);
            $.each(RULES, function(key, fn) {
                // _this.data("dv-" + key) 这样做有点傻,所以在默认配置里,写上dv
                var $fileName = _this.data(this.plugName + "-" + key);
                var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
                // 测试一下输出的内容
                console.log($fileName);
                console.log($fileMessage);
            });
        })
    }
}, "dataValidate")

接下来我们需要用到call来调用函数,并且改变this指向

做个测试:

var RULES = {
    "reg": function() {
        console.log(this);
    },
    "required": function() {
        console.log(this);
    },
    "min-length": function() {
        console.log(this);
    },
    "confirm": function() {
        console.log(this);
    }
}
$.each(RULES, function(key, fn) {
    var $fileName = _this.data(this.plugName + "-" + key);
    var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
    if ($fileName) {
        // fn.call()
        // fn.call(this)
        fn.call(_this)
    }
});

看明白了this的指向之后,我们就要开始着手完成默认的校验规则

var RULES = {
    "reg": function(data) {
        return new RegExp(data).test(this.val());
    },
    "required": function(data) {
        return this.val();
    },
    "min-length": function(data) {
        return this.val().length > data;
    },
    "confirm": function(data) {
        // 思考一下密码一致的校验该怎么做?
    }
}
$.each(RULES, function(key, fn) {
    var $fileName = _this.data(this.plugName + "-" + key);
    var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
    if ($fileName) {
        var result = fn.call(_this, $fileName);
        if (!result) {
            // 进行报错处理
        }
    }
});

校验是否一致可以这样做:

"confirm": function(data) {
    var passwordElement = $(":password")[0];
    if (passwordElement.value == "" || this.val() != passwordElement.value) {
        return false;
    } else {
        return true;
    }
}

最后一步,进行报错处理:

this.$file.on(this.initEvent, function() {
    var _this = $(this);
    // 监测是否已经有报错信息,如果有,就全干掉
    _this.siblings('p').remove();
    $.each(RULES, function(key, fn) {
        var $fileName = _this.data(this.plugName + "-" + key);
        var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
        if ($fileName) {
            var result = fn.call(_this, $fileName);
            if (!result) {
                // 直接在后面追加报错信息
                _this.after("<p style='color:red'>" + $fileMessage + "</p>")
            }
        }
    });
})

到了这一步基本上完成了,用户只需要引入我们这个js文件,然后把接口文档交给用户,让用户自定义报错信息和正则校验就可以了。

但是,如果用户想要扩展自己的默认方法,即扩展默认校验规则,比如新增一个max-length,这个时候该怎么办?

按照我们的这种方式,你们觉得能够进行扩展吗?

当然可以,因为我们的方法绑定在jQuery实对象上面,所以就可以使用jQuery的扩展方法的方式进行扩展,并且要告诉用户用这种方式进行扩展

$.fn.dataValidate.extendValidate = function(options) {
    $.extend(RULES, options)
}
<script type="text/javascript">
    // 用户扩展 
    $.fn.dataValidate.extendValidate({
        "max-length": function(data) {
            return this.val().length <= data;
        }
    })
</script>

我们可以配置data-dv-max-length来进行测试。

至此,一个简单的数据校验插件已经开发完成。

总结

我们开发一个需求或者解决一个问题之前,多想想该如何改才会使代码复用性更高,效率更快,而不是一上手就不假思索的用if{}else{}来进行判断修改,这样代码量多了之后,维护起来就很麻烦了。

而我们这种形式的代码,进行数据校验总比每次校验都使用if{}else{}的代码好上千百倍,可维护性,可扩展性都比较好,当然这种代码也不是一朝一夕就能写出来的,需要日积月累,我依旧觉得自己很菜,所以我每天都在学习。

最后,大家一起加油吧。

posted @ 2017-10-31 22:49  梁音  阅读(201)  评论(0编辑  收藏  举报