相关工具
问题描述
一个详情弹窗组件,首次打开显示正常,关掉后再打开,表单值会丢失
排查思路
该组件默认会在关闭时有清空表单项,内部也会监听数据的变动,理论上应该可以正常展示
在监听的地方打断点,可以看到能正常走进来,但是因为 newV instanceof Object
为 false
,数据不能被正常赋值,所以页面会显示空白,沿着调用栈往上走一级看看
可以看到 vue 内也有 isObject
的判断方法,考虑到这两个地方的目的类似,便把组件内的判断也改成了 vue 的方式,修改后表单在多次打开时也能正常显示了,而 newV instanceof Object === false
的根源暂时没有再追究
几天之后,空闲时间又回想起来上面的 false
问题,先是拿 vue 的 watch
验证,写一个简单的空对象 {}
,再修改值后打印结果,正常为 true
,说明没问题,
又想到这个数据是从接口返回的,就拿 JSON.parse
解析一个简单的 json 字符串验证,结果也正常为 true
,也没问题,
但是表格的 tableConfig.data
里,每一项的求值确实都是 false
最后重新回顾数据从接口字符串解析成对象的过程,发现还经过了 json-bigint 处理(这个库是为了解决三方接口里的id数字过大问题引入),
这个库在解析 object
类型的时候,使用了 Object.create(null)
而使用这种方式创建的对象和普通对象会有些差别(如下图)
原型信息的丢失,让上面的 instanceOf
判断失效了,关于原型链及对象创建的相关概念,可以看看《JavaScript高级程序设计》等书;
到这一步,算是找到了 false
的原因,另外在 vue 源码搜一下,也能看到这种写法,网上有关于这种写法的一些讨论,大家也可以自行了解下
另一种场景
在 本地开发环境 配置 app 菜单权限时,还碰到一个异常,这个异常会导致弹窗组件渲染失败
可以通过异常断点功能,继续追查一下
这里组件希望 data
的值为一个 Object
,但实际传入的却是一个 Array
function styleValue (value, type) {
if (type === 'String') {
return ("\"" + value + "\"")
} else if (type === 'Number') {
return ("" + (Number(value)))
} else {
return ("" + value)
}
}
当 vue 想通过上面这个转换方法,提示用户数据格式有问题时,就发生了上面图里的 TypeError
,
这个传入的 Array
数据,由后台接口返回,其内部是一个 Object
对象,也通过 Object.create(null)
创建,
使用 Object.create(null) + ''
可以简单验证下,会发生一样的异常
考虑到项目中这种情况可能较多,最后把 json-bigint 源码复制到项目中,把 object=Object.create(null)
直接修改成了 object={}
延伸思考
像上面这种出异常的代码,也算是比较常见的写法了,
所以也比较好奇,库的作者为什么要使用这种写法来创建对象,随后便找到了这样的说明: https://github.com/sidorares/json-bigint/issues/38
这里提到了一个原型污染的概念(prototype pollution),能看到作者大概也是为了修复漏洞,所以换成了上面的写法(代码库里也有相应的git提交记录)
这里简单贴两篇介绍: