有段时间没有写文章了,因为一直在忙着赶项目,每次想写什么都觉得好累,身心俱疲。可是不写又怕踩过的坑哪天又掉进去了,而且时间一长就忘了当时是怎么解决的,果然是年纪大了么…
问题描述
项目中需要做一个表格行的数据联动,修改号码,会根据输入的值进行判断是否符合规则,符合规则就能够输入;否则就提示错误信息,并将表格的值置为修改之前的值。这里我使用了自定义指令来完成校验和恢复旧值,当元素focus的时候记录值,如果提示错误就恢复刚刚记录的值。
实测了一下,效果杠杠的。
然鹅一切并不是想象的那么美好。
再多测试几个页面,发现有些页面是好的,有些页面却会出现如上图所示的奇怪现象!自定义指令修正了错误值,但是你一点击表格区域,数据就又变成了错误值!
更奇怪的是其他的几个页面却不会出现这个问题!
这奇怪的现象让我想起来原来使用事件绑定来处理数据联动和效验时,也出现过同样的怪像,当时绑定的change事件,理论上我修改完了数据,点击其他任何地方,数据都应该产生联动效果,即根据我设置的值自定计算其他的值。但是实际情况是,我必须点击一下表格中的任意区域,数据才会联动更新!!后来这种方案被boss否决了,这个问题也不了了之。
解决之道
解决问题总是容易的,不容易的是找出问题,我一定要找出这种怪像的根源。
于是在弹窗之前console了一下元素,看看元素结构。
不看不知道,一看吓一跳。
这里可以清楚的看到,该元素拥有一个数据属性和一个访问器属性,然而数据属性value和访问器属性_value的值居然不一样?访问器属性的值是修复后的00551235,而数据属性居然是123!!!难怪自动修复之后,再点击表格的其他区域,数值会变成123!!因为数据属性本身就是123!
那么问题又来了,为什么会出现这种情况?
其实问题应该这样问:为什么访问器属性的值和数据属性的值不同?
那么我是在哪一步赋值的00551235呢?是在提示错误,弹窗之后通过el.firstElementChild.value = oldValue来给value属性赋值的!而上图显示说明,value作为访问器属性,确实被赋值成功了!那么为什么数据属性的值却没有改变呢?
于是又是一顿折腾,终于发现原来rowData.endnumber这个属性原来并不存在,由于服务器返回的对象中,并没有endnumber这个字段,是应要求在前端获取数据之后,手动添加上去了!
而手动添加的代码居然是这样的:
没错,就是这里。(没错,这个坑爹的坑就是我自己埋的)
看尤大的官网,https://cn.vuejs.org/v2/guide/list.html#对象更改检测注意事项
八成是这个坑没错了!
于是使用$set来定义新增的对象属性,问题完美解决!
说来说去,还是因为自己对于vue中对象属性的新增的坑,没有足够的重视。导致了这样一个奇怪的bug。
而关于这个bug的深究,我觉得还是有点意思的,还是双向绑定的原理。因为vue双向数据绑定的原理是通过definePropoty来劫持对象属性,然后在对象属性值变动的时候,调用订阅方法,更新视图。而由于我没有使用$set方法 ,导致新增的数据属性没有被vue初始化生成劫持后的setter和getter方法,导致了后面一系列的问题o(╥﹏╥)o