【调试日志】我只是用wangeditor上传图片而已,页面咋就崩溃了呢~

路很长,可天总会亮的

起因

为什么会有这篇调试日志呢?事情还得从一个简单的需求说起...

上周周二,我按着既定的开发计划,准备开发一个xx功能(啥功能就不说了哈,商业机密哈)的表格加编辑弹窗。功能很简单,不需要复杂的逻辑处理,按照之前的开发流程,就是使用全局的表格、表单业务组件,配置好对应的字段和校验规则,联调一下接口就可以愉快地到掘金学习了(摸鱼.jpg)。

but,就在我开发完功能,便测试功能,边打开保温杯喝一口清香的绿茶时,页面崩溃了~。??,一时间问号充斥在我的脑海,哈?怎么打开编辑弹窗,富文本上传图片,保存一下,再打开页面就炸了。我放下杯子,根据以往经验,刷新页面,再次打开弹窗,这次我没有在富文本里编辑任何信息,填完其它表单项直接保存,关闭再打开,咦,页面正常。但是只要加了富文本里上传图片这个操作,页面就会罢工。shit, 此时我还不知道罪魁祸首是谁,杯子的茶瞬间不香了,甚至只觉得喝到嘴里的茶怎么如此苦涩~

于是乎,便展开了为期两天的破案行动。

Day 1

案发现场搜集线索(查看报错信息)

老话说,"会叫的狗不咬人,咬人的狗不会叫",在咱们开发界,有报错提示的问题就是不咬人的狗,咬人的都是那些没有任何提示,莫名其妙的问题。而我之所以踩了两天坑,遇到的就是一会叫一会不叫的"狗"。

为啥这么说,且听我细细道来。上面提到,我遇到的问题是,打开弹窗编辑表单保存成功后,关闭弹窗再次打开弹窗页面崩溃的问题,当时我第一反应就是按下F12打开调试页面,调试窗口输出一大片红色信息,我的心里长出了一口气,幸亏有报错提示,还有得救,于是,我仔细查看了一下报错的信息,提取了关键语句 【Error: Cannot find a descendant at path [1] in node】

我点进去看哪个地方报的错,结果发现报错的地方是第三方库的文件,按照多年的开发经验,初步判断这是wangeditor这个库的相关文件抛出了这个异常。

这是目前搜集到的线索。

被经验主义带偏的分析方向,"烟雾弹"--响应式失效(踩坑~)

有了上面的报错信息,接下来就是分析过程了。由于受到了经验主义的影响,一看到这种报错,我第一时间怀疑的方向是,输入的数据是否出现了问题。

打开vscode中对应的弹窗代码,逻辑没问题啊,打开时用form来接收数据进行回显,关闭时,清除数据及校验。这是正常的数据处理流程啊~

咦,等等,这里的state.form = {...} 使用了解构语法,我记得这会导致state.form失去响应式,难道说是这个原因,沿着这个思路,用toRefs改造了一下,改成以下代码

emm,这样应该就可以了吧,我又试了报错的操作步骤,打开弹窗,直接在富文本上传图片,填写完剩余表单,提交保存,再打开,没报错!!好家伙,原来是响应式失效的问题啊!!再验证一次,没问题就提交代码咯~,然后我再次打开弹窗,这次图省事,我直接在富文本框原有内容基础上又上传了一张图片,上传保存,再打开,damn!怎么又崩溃了。

md,问题好像没想象的那么简单...

案情进度缓慢,陷入红色泥潭(没有方向的各种log输出,毫无头绪)

老实说,上面的一通分析让我感到很挫败,我意识到业务代码的数据可能不是问题关键,因为在非弹窗里使用富文本上传多张图片并没有问题。我决定分析范围转向项目封装的富文本组件,希望能在这里找到破案的关键。

这部分代码是富文本组件的数据交互部分,我在数据输入和改变的过程中添加log,企图在这些信息中找到蛛丝马迹,但是并没有用,我甚至加了多层的nextTick以及setTimeout进行包裹(这大概就是病急乱投医了),还是没用,页面还是崩溃,红色的报错信息就像巨浪向我袭来,我很惊恐但是无可奈何,只能静静地被它淹没。

不完美的方案,难道就止步于此了吗(v-if解决)

分析了一天,还是没有任何进展,眼看临近下班。我决定先用一个不完美的方案,看看先解决页面崩溃的问题。根据以往经验(又来了,经验主义脑)遇事不决,就用v-if,让整个弹窗组件内容重新渲染,不出所料,成功了。先把代码提交了,起码这样处理,页面不会崩溃,虽然打开的时候会闪一下(内容从无到有渲染,导致高度变化),但是就先这样吧,先保功能可用。下班路上,脑海里还是会不自觉地想起那片红色海洋以及一个疑问,为什么重新渲染就不会报错呢??

Day2

调整分析手段,逼近真相(使用debugger,断点调试错误信息)

时间来到第二天,由于前天的大失败,早餐吃起来也没啥味道,草草应付两口后,打开vscode,继续开整,我就不信了,小小bug还能被它拿捏住。

今天打算使用新的调试手段,没错使用断点调试,之前用过断点debugger,但是只是用来简单查看数据,用来调试分析问题还是第一次。

首先给导致页面崩溃的报错信息打上断点,如下图所示

操作报错流程,打开弹窗--> 填写富文本及相关信息 --> 提交保存 --> 再次打开

此时调试工具会来到刚刚断点的位置

查看if中的条件,看看是哪个导致异常抛出

由下图数据可知,导致错误抛出的是这个条件 !node.children[p] === true

也就是报错描述的 节点根据p 无法找到后代数据

我们看一下children的数据,发现children的数据长度为1,但是p的值是1,也就是索引值为1,children[p] -> children[1],很明显,数组中只有一项数据,索引值1也就是要找数组的第二项数据,这肯定是拿不到数据的,这也是报错的直接原因。

从上面的分析来看,导致报错的原因是p的值出现了问题,也就是外层的path有问题

想知道path出问题的原因,我们得往上溯源,这时候就需要看看调用栈,看看这个报错信息的上一个调用方法是谁,如图所示,打开调用栈,并且需要将被忽略的调用栈打开显示(这一步很关键)

接下来就是抽丝剥茧

在levels这个文件中,发现了path的来源,是由editor的selection得来的

editor是wangeditor的实例,想知道selection的含义,最好就是找官方文档 果然,文档就有关于selection的信息

选区?看到这个名词,我第一时间就是这应该跟光标有关,这么一想,我心里就有了验证思路,我打开了原来的弹窗。

将鼠标放在富文本的任意位置,在关闭弹窗,发现也会报错,跳入断点,有眉目了!!

我们鼠标聚焦的位置会影响selection的数据,而且selection与下面第一张图的位置有关联

selection是用来描述光标信息的,path: [1, 0] 先确定光标的范围,表示在第二块区域的第一块子区域上(wangeditor是基于slate开发的,slate对于富文本会基于它们定的规则进行划分,以类DOM的结构存储富文本数据,这一块不用深究哈),offset:6表示在该区域的第7个位置(0开始)

根据上面的验证,我们可以发现,只要我们将光标放在第二块区域以及之后的区域,在第二次进入弹窗的时候,由于我们对数据做了清空,但是又记录了上一次的selection,导致初始化光标光标从而导致报错。

守得云开见月明(问题解决)

既然知道了问题的根本原因,那解决问题的思路就很清晰了,直接来到富文本的组件中,在文本框失焦时也就是onBlur这个钩子中,将selection重置一下就可以啦~。改完再验证一下,重复之前的报错流程,没问题。验证下非弹窗模式下的富文本,也没受到影响。总算是解决了,悬在心里两天的石头终于可以放下。在这次的破案行动中,让我一直坚持下去的,既有对真相的渴望,也有不服输的钻牛角尖,虽然过程很坎坷,但是当迷雾散去,阳光洒在身上的感觉,真的是美滋滋。

小结

这次的调试复盘,调试手段很重要,分析方向很关键,错误的方向会导致事倍功半甚至是无用功。解决问题的过程真的挺像办案的,得学会抽丝剥茧层层递进。

最后引用一句刑侦名言作为结尾,我觉得调试bug同样适用

Every contact leaves a trace(每次接触都会留下痕迹)

相关推荐
安木夕14 分钟前
C#-Visual Studio宇宙第一IDE使用实践
前端·c#·.net
努力敲代码呀~16 分钟前
前端高频面试题2:浏览器/计算机网络
前端·计算机网络·html
高山我梦口香糖37 分钟前
[electron]预脚本不显示内联script
前端·javascript·electron
神探小白牙38 分钟前
vue-video-player视频保活成功确无法推送问题
前端·vue.js·音视频
Angel_girl3191 小时前
vue项目使用svg图标
前端·vue.js
難釋懷1 小时前
vue 项目中常用的 2 个 Ajax 库
前端·vue.js·ajax
Qian Xiaoo1 小时前
Ajax入门
前端·ajax·okhttp
爱生活的苏苏2 小时前
vue生成二维码图片+文字说明
前端·vue.js
拉不动的猪2 小时前
安卓和ios小程序开发中的兼容性问题举例
前端·javascript·面试
炫彩@之星2 小时前
Chrome书签的导出与导入:步骤图
前端·chrome