记录Failed to execute 'appendChild' on 'Node'报错的解决过程

5,596 阅读4分钟

一、问题描述

某天在开发需求时发现,业务页面上报告了一个Script error。

image.png

由于vconsole看不到详细报错信息,于是在chrome上打开,具体报错信息如下: image-20221115153911127.png

Uncaught DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method. at Object.appendChild (npm.cdn-go.cn/vue/2.6.12/…)

看起来是vue2.6.12包里报了一个错误,但从报错提供的信息看不出来哪行代码引发的报错。

二、解决过程

1、google一下

解决问题的第一步往往是先看看“别人咋解决的”:把错误信息ctrl+c ctrl+v到浏览器里google一下先。

stackoverflow里看到的第一个解决方案是通过添加client-only标签:

<template>
  <div id="container">
     <client-only>
        <Components>
     </client-only>
  <div>
</template>

在项目里也照着这个方法在App.vue里添加client-only标签,重试后发现没有效果,依然报错。

也有说法说是由于标签的关闭写法导致的Hydration errors

image-20221115144614936.png

于是我搜索了下项目代码,发现确实有几处标签是使用类似 <span class="split" /> 写法进行关闭的。抱着尝试的心理,我把这些写法都改成了<span class="split"></span>

然而重试后依然没有解决这个报错。

2acf7fd7-ef89-4713-9b2b-1f34883a8133.jpg

2、断点调试

既然网上的方法没有效果,那只能打断点看看是否能找到报错的具体位置。

从错误报告点击进详情,可以看到是vue.min.js文件的appendChild抛出的错误。

image-20221115145822101.png

在这里打一个断点,并运行:

第一次经过此处:e是一个div,t是一个#comment的node节点,此时还没有抛出错误。

image.png

第二次经过此处:e是一个div,t是一个text元素,此时还没有抛出错误。

image.png

第三次经过此处:e是#comment的node节点,t是一个按钮的div元素,此处抛出了对应的错误。

image.png

也就是在第三次运行appendChild时,由于e此时是一个node节点,不支持appendChild方法,于是vue抛出了Uncaught DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method.错误。

在项目里搜索comment,发现并没有找到相应的代码。于是从第三次appendChild的t参数入手。

image-20221115150838630.png

可以看出来这是一个按钮元素,并且按钮文字内容是“我知道了”。在项目里搜索“我知道了”,可以找到两处符合条件的元素。根据此时的页面位置可以排除captcha组件,所以锁定了verifyResult组件里的“我知道了”按钮。

这处按钮的html代码如下:

<q-button
  v-if="resultInfo.showBtn"
  type="primary"
  @click="resultInfo.callback"
>
  {{ resultInfo.btnText }}
</q-button>

先把这部分内容直接注释掉看看。 发现这个报错确实不见了。可以确认,这个按钮就是报错的源头。

3、定位

那这个按钮到底有啥问题呢?看起来就是一个平平无奇的确认按钮呀。

从刚刚打断点的时候可以看到,此时按钮的text正常取到了"我知道了"文案,且报错信息是在进行appendChild,也就是添加元素的动作时。而这个按钮里用到了v-if,合理怀疑是这个v-if引起的。于是尝试把v-if去掉或改成一个常量,发现页面也不会报错。

所以可以确定,这里的报错是由于v-if的参数引起的。

这里v-if的参数是:resultInfo.showBtn

这里resultInfo是一个计算参数:

image-20221115152233526.png

showBtn是通过另一个计算参数:isMobile来控制的。

image.png

看来问题就在isMobile上。

通过打印isMobile发现,在node时由于没有window,所以isMobile=false,而页面渲染时isMobile=true。

所以这里isMobile有一个从false -> true的变化。

企业微信截图_573802c3-dad3-4556-9cfc-95865994d559.png

企业微信截图_2432e7fe-e144-4477-afbb-042501578aff.png

所以这里按钮元素经历了从无到有的过程,在appendChild的时候node节点就抛出了错误。

4、解决

这里解决方案的思路就是去掉这个从无到有的过程,或者把这个变化后移。

  1. 去掉这个从无到有的过程:把v-if改成v-show
  2. 把这个变化后移:isMobile不使用计算参数,在默认初始化为false,在created时再进行赋值。
created() {
    this.isMobile = browser.isMobile();
}

两种方法都可以解决,我这里使用了第一种解决方案,报错没有再出现了,且页面按钮可以正常显示~

3a1709e4-5293-438f-9cce-69541a6bddf9.gif