以bug为师学习源码

前言

"小蚂蚁,你这个页面不对啊,输入框有值但还是提示没有填写数据",测试妹子对我说,我一点也不耐烦地说:"应该不可能出现这种问题,再说了你这没有截图啊,没图没真相啊",妹子马上发来截图:看这就是你要的截图。

我低下头说:"我看看吧",这一看不要紧从早晨看到下午,从下午看到下班,还是没看出来哪里有问题啊!既然看不出来问题,那么就去看看源码,接下来一顿操作猛如虎;

深入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类型的变量

最后我顺利地解决了这个问题,虽然这个时候已经"夕阳西下",我还是硬着头皮去找测试说:下班之前给它解决了,该你上了!然后就是测试测完了,我们顺利地上线了。

相关推荐
百万蹄蹄向前冲1 小时前
2024不一样的VUE3期末考查
前端·javascript·程序员
alikami1 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
wakangda2 小时前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡2 小时前
lodash常用函数
前端·javascript
emoji1111112 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
一个处女座的程序猿O(∩_∩)O2 小时前
vue3 如何使用 mounted
前端·javascript·vue.js
迷糊的『迷』2 小时前
vue-axios+springboot实现文件流下载
vue.js·spring boot
User_undefined2 小时前
uniapp Native.js原生arr插件服务发送广播到uniapp页面中
android·javascript·uni-app
web135085886352 小时前
uniapp小程序使用webview 嵌套 vue 项目
vue.js·小程序·uni-app
麦兜*3 小时前
轮播图带详情插件、uniApp插件
前端·javascript·uni-app·vue