前言
"小蚂蚁,你这个页面不对啊,输入框有值但还是提示没有填写数据",测试妹子对我说,我一点也不耐烦地说:"应该不可能出现这种问题,再说了你这没有截图啊,没图没真相啊",妹子马上发来截图:看这就是你要的截图。
我低下头说:"我看看吧",这一看不要紧从早晨看到下午,从下午看到下班,还是没看出来哪里有问题啊!既然看不出来问题,那么就去看看源码,接下来一顿操作猛如虎;
深入iview源码
有时候迫不得已必须去看源码,源码有助于分析问题的本质;我这个表单页面用的vue2+iview,那么这就用到了Form和FormItem组件,问题肯定出在他们身上;但是怎么从本地代码断点调试到iview源码呢?
这就需要用到软链,我们在本地clone一份iview源码,然后修改packagejson中的main字段,改成src/index.js,这样就可以调试源码了,然后执行npm link;呀,发现报错了,原来iview还在使用webpack3,gulp的版本比较低,所以node也需要降低到11.15.0这个版本才行,降了版本再执行就OK了;
再到项目文件夹下执行npm link iview,成功链接到iview源码上去了,发现有个别源码的错误,凭借感觉先修复一下,比如下面这个改成esm导出:
找到执行校验的函数:一般都是调用$refs.form.validate方法,打断点
点击提交,成功进入断点,接下来就是秀操作的时候了,F11进入函数:
可以看到最关键的几行代码:
js
const validator = new AsyncValidator(descriptor);
let model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, errors => {
this.validateState = !errors ? 'success' : 'error';
this.validateMessage = errors ? errors[0].message : '';
callback(this.validateMessage);
});
iview使用的校验工具是async-validator
,相信很多同学没有使用过这个库,那么我们就来看一看:
async-validator
创建一个mjs,直接使用node跑一下这个脚本:
js
import Schema from "async-validator";
const schema = new Schema.default({
name: {
required: true,
message: "姓名不能为空",
},
age: {
required: true,
message: "年龄不能为空",
},
});
schema.validate(
{
name: "张三",
age: 18,
},
undefined,
(errors) => {
console.log(errors);
}
);
发现没有报错,配置里面支持配置数据的类型,就是相当于在js这个弱类型语言里面加上强类型校验,这在几大框架里面都有做这件事情,js开发者永远都向往着强类型,给age加上一个string类型那么这个时候就会报错[ { message: '年龄不能为空', fieldValue: 18, field: 'age' } ]
diff
age: {
+ type: "string",
required: true,
message: "年龄不能为空",
},
再回到之前的问题上,有没有可能就是类型判断的错误导致即使填充了数据也报错,再打印errors看一看,发现果然如此,FormItem组件中validator中的type默认为string,而赋予的默认值为number类型,导致类型校验不通过而报错;
这也引发我们深思,后端都是强类型语言,比方说有些场景我们需要输入一些数字,这个时候input框内获取到的必然都是字符串,而后端返回给我们的必然又是number类型,这就导致输入输出类型不一致了,本来对于js这个弱类型语言,这一点完全没有必要纠结,反正字符串和数字他们之间隐式转换可以随便转;所以就我而言,js没有必要把字符串和数字类型限制得这么死,就像async-validator
一样,弱类型不需要强校验
同样的async-validator为什么iview就有问题?稍有经验的同学应该一眼就能看出来,那必然是版本的问题,我们安装iview使用的版本:1.12.2
,然后执行同样的脚本报错:[ { message: '年龄不能为空', field: 'age' } ]
,我们再深入到源码里面去看就知道它是默认设置为string类型了,这样的话如果不指定类型,就会当做string类型来校验,是不是感觉好像找到了一个bug,我本来以为可以提一个PR,但是后来想一想找个问题在于async-validator,给iview提PR好像也没什么用啊,而且async-validator在高版本已经解决了这个问题;
后记
在这个解决问题的过程中,我翻看了iview源码,学会了通过软链来调试源码的技巧,同时还掌握了一个异步校验工具:async-validator,可谓一箭双雕
另外我们还发现一个规律就是,在js的世界里面其实string和number这两种类型傻傻分不清,因为他们大部分情况下都可以隐式转换,所以在开发的时候需要格外注意,如果有用到强类型校验的话,我们就需要保证我们变量的类型一致性,比如说一个变量它是string类型那么就不能赋值number类型的变量
最后我顺利地解决了这个问题,虽然这个时候已经"夕阳西下",我还是硬着头皮去找测试说:下班之前给它解决了,该你上了!然后就是测试测完了,我们顺利地上线了。