超轻量代码实现字段校验工具库(移动端)

2018-02-27 11:03:20来源:https://juejin.im/post/5a7e9181f265da4e8c44ffeb作者:稀土掘金人点击

分享


有表单的地方必有校验,我们使用不同的框架有不同的校验方法。在PC端基于React
的Ant Design
框架中的Form
表单的功能就十分强大,而在移动端,却很难找到一款能与之匹敌完整框架。移动端比较流行的vux
框架,虽然有的组件自带了校验功能,但然并卵,基本上很难匹配真实项目的需求。



放弃vux
组件自带的校验功能,后来在github
上找到一款比较流行的校验库vee-validate
。这个库做到了与表单组件之间的解耦,确实也能够灵活的实现各式各样的项目需求,但给我的感觉就是有点重。一般来说,移动端的开销越低越好,代码越轻量越好,区区一个校验,实在不忍心用上如此庞大的工具。



思前想后,还不如自己实现一套校验方案。首先校验工具必须要与组件完全解耦
,其次校验工具要足够轻量,够用就好
。移动端不比PC端,考虑到性能,我们一般校验表单都是在提交的时候进行校验,对不满足要求的字段进行提示(比如:字段组件样式变红,Toast
轻提示等)。


思考

为什么要与组件解耦?



很多时候,我们的表单组件都并非真正意义上的表单,尤其是现在React
和Vue
带来的组件化时代,很多表单组件都是根据我们自己的需求来封装的,各式各样。如果每封装一个组件,就要带上完整的校验功能,那必然是痛苦的,而且有时候不同的第三方组件库很难实现校验的统一性。所以,我们校验的应该是字段
,跟组件本身没有任何关系,尽管我们的字段取值是来自于组件。如此一来,我们的校验工具就适用于任何组件,不管它是否是真正意义上的表单,只要将这个组件跟校验的字段对应起来即可。所以,我们其实是对字段的校验。


如何更轻量的实现对字段的校验?



理想的方式应该是这样的:校验结果 = 校验函数(校验目标集合, 校验规则),用代码实现是这样的:result = validator(target, rules);
。我们只要在提交表单的时候执行校验函数,传入待校验的字段集合
和一套校验规则
,拿到最后的校验结果
。最后,根据结果来做一些后续的处理。


目标集合、校验规则和校验结果如何定义?



一个表单存在多个字段,目标集合
应该就是一个包含所有字段的一个对象。


{ name: '小明', age: 16, email: 'xiaoming@qq.com' }


不同字段往往有不一样的校验规则,校验规则
需要对每一个字段进行定义,因而也应该是一个包含所有字段的一个对象。


{ name: 规则1, age: 规则2, email: 规则3 }


对规则
的定义,我们一般的需求有为空判断
、字符串是否满足条件
、数字是否在指定范围之内
、其他特殊处理
。总结起来,校验类型可以归为3类:为空、正则、自定义。为空判断
最为常见,单独归为一类;正则表达式
可以满足大多数的校验规则,可归为一类;前两种基本上已经满足了百分之七八十的业务场景,所以将剩下的所有校验规则通过自定义函数
实现。所以最后每一个字段的校验规则是这样的:


{
required: true,
pattern: /^QQ/w{2,5}$/,
validator() {
// TODO...
}
}


当我们拿到校验结果
,我们想要知道校验结果是否通过
、校验结果中不通过的字段以及每个字段对应的提示语
。所以,校验结果应该是一个包含所有不通过字段的对象。


const result = { name: '姓名不能为空', age: '年龄必须大于18岁' }


对于校验结果,我们或许只关心本次校验是否通过
,仅仅拿到单纯的校验结果对象不便于操作。因此,result
应该还有一个hasError()
函数用于返回校验结果是否出错
,另外还应有一个first()
函数用于返回第一个错误字段的提示信息


result.hasError() // true
result.first()// 姓名不能为空

根据不同的需求,可能还应该提供其他的操作方法。


实现
校验函数(validator)

思路:


遍历规则集合,拿每一条规则去校验目标集合中对应的字段;
首先是为空
校验,空的定义应该是:null
、undefined
、[]
、{}
、''
等;
若为空校验不通过,记录提示文本信息并跳过其他校验,否则继续下一个校验;
然后是正则
校验,同理;
最后是自定义
校验,自定义校验函数应该传入当前校验的值和整个目标的集合对象(可能会存在与其他字段作比较等)。
function validator(target, rules) {
const ruleKeys = rules ? Object.keys(rules) : []
if (!ruleKeys.length) return new Result()
const results = ruleKeys.reduce((errors, key) => {
let value = target[key]
let tips = null
const { required, pattern, validate, alias = key, message = `请输入正确的${alias}`, trim = true } = rules[key] || {}
// 去掉字符串首位空格
trim && typeof value === 'string' && (value = value.trim())
if (typeof value === undefined || value === null || !value.length || JSON.stringify(value) === '{}') {
required && (tips = typeof required === 'string' ? required : `请输入${alias}`)
} else if (pattern && pattern instanceof RegExp && !pattern.test(value)) { // 正则校验
tips = message
} else if (typeof validate === 'function') { // 自定义校验函数
const res = validate(value, target)
tips = typeof res === 'string' ? res : (!res ? message : null)
}
return tips ? { ...errors, [key]: tips } : { ...errors }
}, {})
return new Result(results)
}
校验结果(Result)


我们看到,在validator
函数中,返回了Result
的实例对象。代码很简单:


class Result {
constructor(errors = {}) {
Object.assign(this, errors)
}
hasError() {
return Object.keys(this).length > 0
}
first(index = 1) {
return Object.values(this)[index - 1]
}
firstKey(index = 1) {
return Object.keys(this)[index - 1]
}
}

其实也就是在普通的对象上,扩展(原型上添加)了3个方法。


API
validator


核心校验函数:validator(target: Object, rules: Object) => result: Result


target


待校验的目标对象集合:{ name: 'Kevin', age: 18 }


rules


校验规则集合:{ name: rule1, age: rule2 }


alias
:字段别名。比如:姓名,年龄。作为默认提示语输出,忽略则为key

trim
:是否忽略字符串首尾空格。默认true

required
:是否必须。为字符串时作为提示语输出。
pattern
:正则表达式。
message
:作为校验提示语输出,忽略则输出默认提示语。
validate
:自定义校验函数,validate(value, target)
。返回字符串时作为此次校验不通过的提示语输出,或者返回boolean
表示是否通过本次校验,返回fasle
时输出默认提示语。
Result


包括所有不通过校验的字段集合:{ name: '姓名不能为空', age: '年龄必须大于12岁' }


方法:


result.hasError()
:本次校验是否有错(不通过)。
result.first([index: Number])
:校验结果中第一个字段的提示语。index
指定第几个字段,默认为1

result.firstKey([index: Number])
:校验结果中第一个字段的key
。index
使用同上。
应用


该校验库已经发布到npm仓库,可通过npm
或yarn
工具进行下载。


$ yarn add @moohng/validator
在Vue
中使用
<template>
<x-form>
<x-input label="姓名" v-modal="form.name" />
<x-upload label="头像" v-model="form.avatars" />
<x-select label="性别" v-model="form.sex" />
<x-input-number label="年龄" v-model="form.age" />
<x-button type="submit" @click="onSubmit">提交<x-button>
</x-form>
</tempalte>
<srcipt>
import { validator } from '@moohng/validator'
import rules from './rules'
export default {
data() {
return {
result: null,
form: {}
}
},
methods: {
onSubmit() {
this.result = validator(form, rules)
if (this.result.hasError()) {
this.$toast(this.result.first())
} else {
// ...
}
}
}
}
</script>


假如你需要在校验报错之后对相应的组件进行样式上的处理,可通过响应式的result
去完成:


<template>
<x-form>
<x-input
:class="{ 'error': result.name }"
label="姓名"
v-modal="form.name"
/>
</x-form>
</tempalte>


更优雅的做法是通过一个自定义指令
去完成这些事情,比如一些滚动、聚焦、失焦、样式切换等行为。你需要记住的是:你已经拿到了这个校验结果,这个结果已包含了你需要的信息,且是响应式的(在data
中已预先定义),之后的一切处理都可通过这个result
对象去自行扩展。


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台