背景
现在市面上很多 ui 框架都有 form 表单校验,还有些三方库。我的应用场景是不需要和 ui 绑在一起,传统的提交校验 --- 按顺序,一个个检验。
?
比如我有个接口的参数 params1, params2 需要检验,不为空。
传统方式当然也是最快的 if else
if(!paramsN) { alter(msgN) return false }复制代码
策略模式
自从看了策略模式相关的文章受到了一些启发,同时搬下大佬写的自定义校验器,通过 proxy 实现的。
下面瞻仰一下同时看下如何封装export const validatorCreater = (target, validator) => new Proxy(target, { // 保存校验器 _validator: validator, set (target, key, value, receiver) { // 如果赋值的属性存在校验器,则进行校验 if (this._validator[key]) { // 遍历其多个子校验器 for (let validatorStrategy of this._validator[key]) { let {errorMsg = '', params = []} = validatorStrategy if (!validatorStrategy.validator.call(null, value, ...params)) { throw new Error(errorMsg) } } } // 赋值语句放最后,如果失败不赋值,如果不存在校验器则赋值 return Reflect.set(target, key, value, receiver) }})复制代码
如何使用
_checkValue = ({ parentId, baseInfo }) => { // 这里参考策略模式 const isNotEmpty = val => val && (val + '').length > 0 const objEmpty = val => !_.isEmpty(val) let validators = { baseInfo: [{ validator: objEmpty, errorMsg: 'xxxx' }], parentId: [{ validator: isNotEmpty, errorMsg: 'xxxx' }] } const checkObj = validatorCreater({}, validators) try { checkObj.parentId = parentId checkObj.baseInfo = baseInfo } catch (e) { console.warn(e) message.error(e.message) return false } return true }复制代码
怎么样,看完是不是受益颇多。用了一段时间思考可不可以通过 es5 的方式来实现一个简版的呢
通过深思熟虑,还是想出了一个雏形方案如下精简版
export const checkParams = (rules = []) => callback => { let checkStatus = true try { for (let i = 0; i < rules.length; i++) { const { fn, value, errorMsg } = rules[i] if (!fn(value)) { checkStatus = false callback && callback(errorMsg) break } } } catch (error) { console.error(error) console.warn(` 所属值类型------ fn: 校验函数 value:所需校验值类型 errorMsg: 错误信息 `) } return checkStatus}复制代码
外部调用
_checkParams = ({ inquiryPrice, inquiryUnit, sellStockNum }) => { const isNotEmpty = val => val && (val + '').length > 0 const rules = [ { fn: isNotEmpty, value: inquiryPrice, errorMsg: '请填写1' }, { fn: isNotEmpty, value: inquiryUnit, errorMsg: '请选择2' }, { fn: isNotEmpty, value: sellStockNum, errorMsg: '请填3' } ] return checkParams(rules)(message.error) }复制代码
改进
判断为空和正则是最常用的功能,优化一下如下
const _ = require('lodash')export default (rules = []) => callback => { let checkStatus = true try { for (let i = 0; i < rules.length; i++) { const { fn, value, errorMsg = '该参数不合法', required, pattern } = rules[i] // 只校验为空 if (required && !isEmpty(value)) { checkStatus = false callback && callback(errorMsg) break } // 都需要校验正则了肯定先判空 if (pattern && isEmpty(value) && !patternCheck(pattern, value)) { checkStatus = false callback && callback(errorMsg) break } // 自定义校验函数 if (fn && isEmpty(value) && !fn(value)) { checkStatus = false callback && callback(errorMsg) break } } } catch (error) { console.error(error) console.warn(` 所属值类型------ fn: 自定义校验函数 value:所需校验值 errorMsg: 错误信息, required: 是否必填, pattern: 正则 `) } return checkStatus}const isEmpty = (value) => { const types = ['string', 'number'] const isExit = types.includes(typeof value) if (isExit) { return (value + '').length > 0 } else { if (!value) { // 判断 null, undefined return false } else { // 判断空数组、空对象 return !_.isEmpty(value) } }}const patternCheck = (reg, value) => { if ((typeof value) === 'string') { return reg.test(value) } else { console.warn('正则只对string有效') return false }}复制代码
使用
const rules = [ { value: 1, required: true}, { value: -1, fn: (val) => val > 0, pattern: /^\d{1,}$/ }, { value: -1, fn: (val) => val > 0 } ]checkParams(rules)(console)复制代码