【调试日志】我只是用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(每次接触都会留下痕迹)

相关推荐
崔庆才丨静觅12 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅13 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax