javascript表單驗證

Link-X發表於2018-11-28

讓我們來看看更優雅的表單驗證吧

大家看到這段程式碼有何感想。

javascript表單驗證

有同學會問,這有問題嗎?沒問題。只是不怎麼好看而且有些難以維護。

那麼我們來看看有沒有更好的方式吧。
本文所以的程式碼都在這個連結。 github.com/Link-X/veri…
首先程式碼未動,文件先行我們先來看下這一坨東西

// 變數
 var obj = {
     number: 1,
     string: 'str',
     array: [1, 2, 3],
     object: {a: 1},
     date: '2018-10-10 08:08'
  }
  
  // 校驗規則
var rule = [
     number: [
        { required: true, message: '請輸入number' }
     ],
     string: [
         { required: true, type: 'number' message: '請輸入string' }
     ],
     array: [
         { message: '請輸入array' },
         { min: 0, max: 3, message: '最小為0,最大為3' }
     ],
     object: [
         { required: false,  message: '請選擇obj', validator: judgeObj }
     ],
     date: [
         { type: 'date' required: true, message: '校驗時間是否合法時,type必須為date' }
     ]
  ]
  // 自定義校驗規則
    function judgeObj (val, cb)  {
      console.log(val)
      if (val.a > 1) {
          cb()
      } esle {
          cb(new Error('不通過'))
      }
  }
複製程式碼

現在我們的需求是有一個變數,有一個規則,規則對應的key去校驗變數
總之就是配置校驗,減少if else。拯救髮際線。

那麼激動人心的時候到了。開始搬磚寫程式碼
檔案中。為了讓程式碼看著更簡單,我們就當所有的人都不會使用出錯,所以不會對傳入的引數進行校驗是否錯誤,有興趣的同學可以自己提交

首先,定義一個class

class Verify {
    constructor (data, rules) {
        this.data = null
        this.rules = {}
        // 每次例項化的時候 把需要校驗的規則和變數匯入
        this.$init(data, rules)
    }
    $init (data, rules) {
        // 初始化函式
        this.data = data
        this.rules = rules
    }
    iterator (rules) {
        // 核心函式,校驗所以的規則
        
        // 狀態變數
        let status = {
            status: false,
            message: '',
            key: ''
        }
        // 迴圈迭代
        for (let v of Object.keys(rules)) {
            // 這裡我們也簡單點把,兩個規則當一個去校驗
            const judge = { ...this.rules[v][0], ...this.rules[v][1] }
            console.log(judge) // 此時我們就能獲取到所有的規則啦
            console.log(this.data[v]) // 所有的資料
        }
    }
}
複製程式碼

那麼我們已經完成了初始化,並且能通過遍歷獲取到所有的規則和資料。接下來要做的事情就是,將他們一一對應,並且校驗。

孩兒們,躁動起來
我們重點寫一下這個 iterator 函式,這個寫好了,就完成了一半

// 我們抽出兩個函式來執行,自定義規則校驗吧,不如iterator太大了
class Verify {
    ...
    ...
    iterator () {
        // 核心函式,校驗所以的規則
        
        // 狀態變數
        let status = {
            status: false,
            message: '',
            key: ''
        }
        // 迴圈迭代
        for (let v of Object.keys(rules)) {
            // 這裡我們也簡單點把,兩個規則當一個去校驗
            const judge = { ...this.rules[v][0], ...this.rules[v][1] }
            const val = this.data[v]
            if (toString.call(judge.validator) === '[object Function]') {
                // 自定義規則如果有的直接用自定義規則
                // 自定義校驗,對於的處理方式
                judge.validator(val, ((status) => {
                // 自定義執行函式。目的就是為了把 狀態變數給自定義規則
                    return (cb) => { 
                        status.status = !!(cb && cb.message)
                        status.message =  cb && cb.message
                     }
                })(status))
            } else if (judge.required) {
                // 自定義規則必填設定了 true
                status.status = this.verifyTop(judge, val) || this.verifyBottom(judge, val)
            } else if (!judge.required && judge.min && judge.max) {
                // 自定義規則設定裡最大最小值
                status.status = val && this.verifyBottom(judge, val)
            }
            
            if (status.status) {
                // status 為true停止校驗
                status.key = v
                status.message = status.message ? status.message : judge.message
                return
            }
        }
    }
}
複製程式碼

好啦,這個時候核心函式 iteratior 已經寫完了。接下來我們主要的任務就是處理 this.verifyTop 和 this.verifyBottom 這兩個自定義規則校驗。 這裡兩個函式需要用到很多,js變數的型別校驗.

接下來我們新建一個檔案,就叫utils.js吧,把所以的型別校驗都放在那裡。

// 直接上程式碼,簡單粗暴.這些校驗大家應該都不陌生吧。
export const isArray = (data) => {
    // 判斷是否陣列的
    return Object.prototype.toString.call(data) === '[object Array]'
}

export const isNumber = (data) => {
    // 判斷是否數字的
    return Object.prototype.toString.call(data) === '[object Number]'
}

export const isString = (data) => {
    // 判斷是否字串的
    return Object.prototype.toString.call(data) === '[object String]'
}

export const isBoolean = (data) => {
    // 判斷是否布林值的
    return Object.prototype.toString.call(data) === '[object Boolean]'
}

export const isFunc = (data) => {
    // 判斷是否函式的
    return Object.prototype.toString.call(data) === '[object Function]'
}

export const isObject = (data) => {
    // 判斷是否函式的
    return Object.prototype.toString.call(data) === '[object Object]'
}

export const isNull = (data) => {
    // 判斷是否null的
    return Object.prototype.toString.call(data) === '[object Null]'
}

export const arrayLen = (data) => {
    // 判斷陣列有木有長度
    return isArray(data) && data.length
}

export const objectLen = (data) => {
    // 判斷物件有木有函式
    return isObject(data) && Object.keys(data).length
}

export const isDate = (data) => {
    // 判斷是否合法時間的,這個有些不嚴謹哈
    return !!data && new Date(data).toString() !== 'Invalid Date'
}

export const verifyDate = (val) => {
    // 這個是判斷,長度為2的陣列,裡是不是有兩個時間。(時間範圍的時候會用到)
    return isArray(val) ? (isDate(val[0]) && isDate(val[1])) : isDate(val)
}

export const getLen = (val) => {
    // 獲取變數長度
    return val && val.length
}
export const getObjLen = (val) => {
    // 獲取物件長度
    return Object.keys(val).length
}
export const getNumLen = (val) => {
    // 獲取number
    return val
}

export const getType = (val) => {
    // 獲取變數型別
    return (val && val.constructor.name.toLowerCase()) || 'string'
}

export const typeOfS = {
    array: isArray,
    object: isObject,
    number: isNumber,
    string: isString,
    boolean: isBoolean,
    date: verifyDate
}

export const getTypeLen = {
    array: getLen,
    object: getObjLen,
    number: getNumLen,
    string: getLen,
    date: getLen
}
複製程式碼

接著我們會到 index.js。現在我們再改造一下verify 全貌是是這樣的

import {
    objectLen,
    isObject,
    typeOfS,
    isFunc,
    getTypeLen,
    getType
} from './index.js'
class Verify {
    constructor(data, rules) {
        this.data = null
        this.rules = {}
        this.$init(data, rules)
    }
    $init(data, rules) {
        this.data = data
        this.rules = rules
    }
    verifyTop (obj, val) {
        // 校驗第一個規則
        const type = obj.type ? obj.type : getType(val)
        const func = typeOfS[type]
        return !(val && func(val))
    }
    verifyBottom (obj, val) {
        // 校驗第二個規則
        const section = obj.min && obj.max && obj.type !== 'date'
        if (!section) return false
        const type = getType(val)
        const len = getTypeLen[type](val)
        const lenSection = (len >= obj.min && len <= obj.max)
        return !lenSection
    }
    iterator (rules) {
        if (!isObject(rules)) {
            return
        }
        let status = {
            status: false,
            message: '',
            key: ''
        }
        for (let v of Object.keys(rules)) {
            const judge = { ...this.rules[v][0], ...this.rules[v][1] }
            const val = this.data[v]
            if (isFunc(judge.validator)) {
                // 自定義規則如果有的直接用自定義規則
                // 自定義校驗,對於的處理方式
                judge.validator(val, ((status) => {
                // 自定義執行函式。目的就是為了把 狀態變數給自定義規則
                    return (cb) => { 
                        status.status = !!(cb && cb.message)
                        status.message =  cb && cb.message
                     }
                })(status))
            } else if (judge.required) {
                status.status = this.verifyTop(judge, val) || this.verifyBottom(judge, val)
            } else if (!judge.required && judge.min && judge.max) {
                status.status = val && this.verifyBottom(judge, val)
            }
            if (status.status) {
                // status 為true停止校驗
                status.key = v
                status.message = status.message ? status.message : judge.message
                return status
            }
        }
        return status
    }
    validate (cb) {
        const status = this.iterator(this.rules)
        const result = status.status || status.message
        cb({
            result: result,
            key: status.key,
            message: status.message
        })
    }
}
複製程式碼

okok 大功告成。使用方式

用法

<script src="./utils.js"></script>
<script src="./index.js"></script>
var obj = {
    number: 1,
    string: 'str',
    array: [1, 2, 3],
    object: {a: 1},
    date: '2018-10-10 08:08'
 }
 var rule = [
    number: [
       { required: true, message: '請輸入number' }
    ],
    string: [
        { required: true, type: 'number' message: '請輸入string' }
    ],
    array: [
        { message: '請輸入array' },
        { min: 0, max: 3, message: '最小為0,最大為3' }
    ],
    object: [
        { required: false,  message: '請選擇obj', validator: judgeObj }
    ],
    date: [
        { type: 'date' required: true, message: '校驗時間是否合法時,type必須為date' }
    ]
 ]

var judgeObj (val, cb)  {
     console.log(val)
     if (val.a > 1) {
         cb()
     } esle {
         cb(new Error('不通過'))
     }
 }
var main = new verify(obj, rule);

main.validate((e) => {
    if (e.result) {
        alert('succes')
        return
    }
    alert('err')
    console.log(e.key + 'err' + e.message)
})


main.$init(data, rule)
複製程式碼

注意,如果你將需要校驗的值重新覆蓋了,或者校驗規則重新覆蓋了,而沒有呼叫$init 的話,將無法校驗

相關文章