来看看 element-ui 中 form 表单 的验证
前言
element-ui 这个 ui 库许多前端开发都用过, 它的口号的是 网站快速成型工具 上周在使用的过程中,
form组件配置了所需的校验配置项,但是validate校验一直失败,今天来深入了解一下
首先来看,form 在 element-ui 中的用法,更多用法👉 element-ui#form
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
>
<el-form-item label="Password" prop="pass">
<el-input v-model="ruleForm.pass" type="password" autocomplete="off" />
</el-form-item>
// 提交按钮
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)">
Create
</el-button>
</el-form-item>
</el-form>
<script lang="ts" setup>
const ruleFormRef = ref<FormInstance>()
// 数据源
const ruleForm = reactive({
name: 'Hello',
})
// 定义校验规则
const rules = reactive<FormRules>({
pass: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
]
})
// 执行校验
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
console.log('submit!')
} else {
console.log('error submit!', fields)
return false
}
})
}
</script>
在上述代码中 :
el-form 中接收配置 model,rules 属性
el-form-item 接收 prop 属性,这个属性必须存在于 rules 中
el-input 中使用 v-model 连接 model 中的某一个属性
在校验的时候,使用的 el-form 中的 validate 方法
起步
在执行上述代码 validate 的这个地方 打一个 debugger , 来大致过一下流程

- 首先来到
form.vue文件,在form.vue文件中执行validate方法,接收一个callback,然后去执行了validateField(undefined, callback)(🔥 注意: 第一个参数是 undefined,第二个才是callback)
form.vue

- 来到
validateField函数。其中的第一个参数modelProps(默认值[])(刚才的 undefined 被重置为[]), 第二个参数是我们熟悉的callback函数
函数内部 执行 doValidateField 方法,把我们刚才的 modelProps作为入参传入,返回值 result 作为 callback 的实参传入, 🔥 也就是说,这个 result 决定 validate 是否校验成功

这个就是大致的整体流程,validate --> validateField --> doValidateField
核心就是在 doValidateField 里面, doValidateField 的返回结果决定着校验的结果
🔥 el-form 辅助校验
1. doValidateField 起点
依然是接收一个参数props,对于我们的简单例子来说,是一个空数组
内部的 obtainValidateFields 方法接收了 props
obtainValidateFields 返回一个 fields 数组
对 fields 进行判断
- 如果是空,直接返回 true
- 如果不为空,则进行校验,把错误信息收集到
validationErrors对象上,如果没有错误,也是返回true,否则就是返回Promise.reject

内部方法obtainValidateFields 返回的 fields 是一个核心方法,接着看它做了什么
2. obtainValidateFields
可以看到,这个方法拿着 props 走进 filterFields 方法中,但是出现了一个新的变量 fields,这个是从哪里来的呢?🧐

不得不提一下 fileds,因为这个属性很重要,我们稍微偏移下轨道🚆
- fileds 的身世
el-form
const fields: FormItemContext[] = []
const addField: FormContext['addField'] = (field) => {
fields.push(field)
}
ok, 在 el-form 中定义了 addField 方法,但是它自己却没有用到,看到这里,想必有经验的同学已经猜到了,这个方式是为了收集子组件(也可能不是直接子组件) el-form-item 中的属性
果不其然,在 form.vue 文件最后,使用了 provide 方法把 addField 提供了出去👮♀️ ( 这个地方也解释了formEl.resetFields()的来源 )
👇 是 provide 提供的部分方法
provide(formContextKey,reactive({
resetFields,
clearValidate,
validateField,
addField,
}))
既然知道了el-form-item 要使用,那么来到el-form-item文件,(为了方便理解,只保留了重要属性)
el-form-item
const formContext = inject(formContextKey, undefined)
const context: FormItemContext = reactive({
// ....
resetField,
clearValidate,
validate,
})
onMounted(() => {
if (props.prop) {
formContext?.addField(context)
}
})
addField 把 context 属性在 onMounted 的时候全部给拿走了
ok,这个就是 fields 的前世今生,fields 就是一个一个 el-form-item 中的属性方法组成的数组集合,明白了这个,我们回到正轨🚆
在上文的 ### obtainValidateFields 方法中,用到了一个工具函数 filterFields
filterFields
这个不在 form.vue 文件中,在同级目录 utils 文件
uitls.ts

由于props是一个 [],那么 normalized 也是一个空数组,所以,filterFields在这种情况下返回的就是fields 数组
👇图 为 fields 数组

🚀 校验
绕了一大圈,又回到了这里来,是不是已经忘了这个方法,没关系,我们来回看一下,(element-puls调用链是挺长的)
obtainValidateFields 结束了,返回一个 fields, 进行遍历 fields,使用 try catch 包裹捕获错误

这个是校验的代码,执行 filed(即form-item) 中的 validate 方法
每一个 form-item 单独执行自己的校验,然后结果由 el-form 捕获
ok, 来看看 form-item 的 validate 方法
🔥🔥 el-form-item 核心校验
el-form-item
validate
const validate: FormItemContext['validate'] = async (trigger, callback) => {
const rules = getFilteredRule(trigger)
console.log("🚀~ rules:", rules);
const hasCallback = isFunction(callback)
//...
return doValidate(rules)
.then(() => {
callback?.(true)
return true as const
})
.catch((err: FormValidateFailure) => {
const { fields } = err
callback?.(false, fields)
return hasCallback ? false : Promise.reject(fields)
})
}
validate 在 el-from 中只接收了 一个 空字符串'',那 trigger 就是一个 空字符, callback 被赋值为 undefined
这个 validate 方法在 el-input 中也需要用到,那个时候 的 trigger 要换成 blur或者 change 事件了
内部方法getFilteredRule 拿着这个 trigger 返回了一个过滤后的 rules, 最后使用 map 把 trigger给去掉了
👇为过滤后的 rules结果, 基本上没有变化,把 trigger:blur 去掉了

上文用到的 getFilteredRule 方法,过滤 rules, 不做过多描述

doValidate 拿着过滤后的 rules 去做 valide (不得不说,函数名字起的真好👍)
压力来到了doValidate 方法,它接收过滤后的 rules,doValidate是一个 promise方法,不仅需要在 then 方法中执行 callback(true),还需要在 catch 抛出错误
👨🚀 doValidate
重要! 重要! 重要!
是核心的校验方法,为了便于理解, rules 和 modelName 都以注释的形式放上去了,
**使用AsyncValidator进行校验 rules和modelName **

AsyncValidator 是何许人也?
放一段 npm#AsyncValidator 官网上的代码,看完就明白了,el-form 的校验全靠它了

对照着我上文贴的 代码,Shema 替换成 AsyncValidator,description 替换成
[modelName]: rules


偏离一下轨道🚆,讲解一下 fieldValue 的来源

记得formContext 的来源吗?
formContext 来自于 el-form 中的 provide值,model 就是定义在 el-form
的那个大对象,props.prop 是定义在 el-form-item 上的具体属性
当知道了 fieldValue.value 之后,我们 回到正轨 🚆
当 校验成功之后,会走进validator 中的 then 回调,执行 onValidationSucceeded
// 错误消息
const validateMessage = ref('')
// 验证状态
const validateState = ref<FormItemValidateState>('')
const setValidationState = (state: FormItemValidateState) => {
validateState.value = state
}
const onValidationSucceeded = () => {
setValidationState('success')
}
const onValidationFailed = (error: FormValidateFailure) => {
const { errors, fields } = error
if (!errors || !fields) {
console.error(error)
}
validateMessage.value = errors
? errors?.[0]?.message ?? `${props.prop} is required`
: ''
}
onValidationSucceeded 和 onValidationFailed 中 使用 方法setValidationState 设置了不同的校验状态
在 onValidationFailed 中设置了 validateMessage 错误消息
在页面上使用
传递了一个具名的 error 插槽 ,同时把validateMessage 给抛出来

以上就是当你按下 开始校验按钮 之后的全部过程
有朋友要问了,你说的是 点击校验按钮 才能校验,那有时候blur / change 也可以执行校验,是为什么呢?
别急,还记得 elf-form-item 中的 validate 方法吗?,也就是 el-form 执行el-form-item 的校验方法,当你按下 校验按钮 的时候, trigger 为 空,但是 input 中的 trigger 就不一样了
input.vue




通过 inject 获取 el-form-item 实例,执行 validate 方法,这样就可以校验啦
总结
el-form 的校验简单来说,是通过 el-form 身上的 model 对象,匹配 el-form-item 的 prop 属性
即 fieldValue,使用 async-validator 中的实例 validate 拿着 el-form 中传递的 rules 进行 校验
const modelName = prop;
const fieldValue = model[prop]
const validator = new AsyncValidator({
[modelName]: rules,
})
也可以通过 el-input 中执行 el-form-item 的 validate 方法来执行校验
如果有错误,在 el-form-item 中显示
以上就是 el-form 中的关于表单验证总结
转载自:https://juejin.cn/post/7221001498753695805