(是時候開發屬於自己的外掛了)資料校驗外掛開發指南

樑音發表於2019-02-22

在看完了《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="^1d{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{}的程式碼好上千百倍,可維護性,可擴充套件性都比較好,當然這種程式碼也不是一朝一夕就能寫出來的,需要日積月累。

最後,大家一起加油吧。

相關文章