生活[吐槽] 有道居然在Linux下的chrome中出错了???

116 阅读4分钟

我本想通过有道翻译一些句子,没想到上来就不显示,我还以为是兼容性有问题呢,一看控制台居然报错了 image.png image.png 根据打印的信息来看是个空指针异常

image.png

分析该调用栈,它是向获取一个组件的emitOptions,这个函数是vue3.0的事件声明方式,而且下面属性的监听使用的也都是Proxy

image.png 上述代码确实不足以报错,又调试了第二遍,发现t.component在第二次执行的时候会变为空值,而这就引发了null.emitsOptions,必然是报错的。

image.png

上面这段代码定位在Vue3.0packages/runtime-core/src/componentRenderUtils.ts文件中的shouldUpdateComponent方法,该方法的作用是判断是否应该更新组件

image.png 该方法在packages/runtime-core/src/renderer.ts文件夹的updateComponent中被调用

image.png 仔细分析下shouldUpdateComponent文件,该函数的作用是检测对比vnode中的props,childrendirs,transition等属性决定是否更新自组件,该方法对.emitOptions做了强制类型断言(不知道为什么需要这样写),而报错的地方就是prevVNode也就是旧的虚拟dom节点,而新的虚拟dom是具备component的,这也验证了地下描述的为什么会第二遍才报错

image.png

新旧虚拟dom对比

image.png

继续返回上一层,找到patchBlockChildren函数,该函数接受新旧dom数组,并进行比对决定是否更新组件

image.png 可以看到t和e存在所有的组件

image.png

为什么是第二遍才报错呢

我们看首图可知,翻译框是由两部分组成分别是左侧原文和右侧译文,那么我们走第一遍时处理的就是左侧原文,而进行第二次处理时,走的的就右侧,但是右侧的数据还没加载完毕导致获取这个组件的时候还没生成或进行了其它判断,导致组件的初始化异常或执行异常(以上纯属个人猜测)

这个t是什么呢

image.png

查看调用栈,t表示的是一个虚拟dom,是一个叫做VoicePlayer的组件,根据翻译表示的是语音播放器。看来又是哪位大神写的了。我们在VociePlayer的setup函数中打下一个断点,在刷新页面时执行了一次,但是翻译过程中并未再次进入该初始化函数。这个可能是某种v-if等写法的条件判断导致的组件未能初始化。

左侧的发音器虚拟dom为

image.png

右侧发音器虚拟dom为

image.png

如果想继续排查我们需要返回到父节点

组件获取不到大概率也是父组件的问题。使用 t.ctx.parent可以获取父节点信息。父节点是一个文本组件,叫做TextTranslate

image.png

父节点包含了 VoicePlayer组件,看着还行,估计使用的是options+setup的混合写法。

image.png

这是该组件的setup函数

image.png

其实分析到这里原因已经很明确了,那就是新的vnode在执行更新时被销毁或被其他方式隐藏了,而导致执行shouldUpdateCOmponent函数时,由于那种强制类型的写法而产生的报错。是否应该加一个非空校验呢???

最后看一下入口函数吧,如果实例已经挂载,则执行更新操作,否则执行挂载操作,问题还是出在旧虚拟dom上。

image.png 该函数是setup渲染副作用。

根据这篇博客分析CSDN博主,可能存在的原因有三种

分别是

情景解释解决方案
① v-if 导致在 v-if 值为 false 时,如果操作了 v-if 控制的 DOM,可能会因为该 DOM 元素不存在而报错。v-show 替换 v-if
② el-dialog 组件导致默认弹框是关闭的,DOM中没有弹框中的内容。打开弹框再关闭后,弹框中的 DOM 元素没有被销毁,可能会因为不该存在的 DOM 元素而报错(我的报错就是由此导致)。给 el-dialog 组件增加 destroy-on-close 属性
③ el-table 组件导致el-table-column渲染时报错,若 scope.row.xx(xx为任意字段值)不存在,对其直接执行 length、toString() 等方法而报错。scope.row.xx?.length

更新

image.png

windows版和Linux版怎么会不同不?,估计是根据系统做判断了,可怜的Linux用户