Failed to execute ‘removeChild’ on ‘Node’: The node to be removed is not a child of this node.
不知道大家见到过这个removeChild报错没有,去年项目设置了错误崩溃的兜底后,发现上线后页面瞬间疯狂报错页面崩溃,而且根本找不到头绪,报错原因就是这个removeChild找不到节点,真的要碎了。
本地根本没有办法复现,根据报错的UA分析,更是所有机型都存在,如果遇上用户反馈,我们只能建议他们换个浏览器试试。
1. 解决原因:空节点
根据github上的查找原因,参考:https://github.com/facebook/react/issues/17256
是因为代码中使用的空节点增删的原因,也就是<></>
,<Fragment>
这个东西导致的。在移除空节点的时候react识别认为此处的节点已经为空,导致抛出异常。(当然我们自己的电脑从没出现过。。。)
目前并不清楚其内部为什么会错误识别空节点,**但是我们唯一能做的就是:禁止项目内使用空节点。**或者可以使用,但是不允许在判断条件中,三元符号的判断里,可能不知道原因就异常了。
但是很绝望就是及时我们改掉了所有的空节点,这个问题仍然没有解决,还是会报错removeChild 、 insertBefore无法识别。
2. 解决原因:文本节点
根据一阵定位,最终我找到了错误原因(之一):文本节点。它同样会可能被异常认为由空标签包裹的子节点。
比如上一个解决的错误代码:
{
isText ? <span>111</span>: isNumber ? 111 : <span>test</span>
}
看出错误原因了吗?因为动态渲染中,如果为true,渲染的是一个span标签,而判断为false,渲染为一个纯文本节点。
文本节点和html节点的虚拟DOM节点类型是不一样的。
React删除或者插入纯文本节点时,可能由于上一个span标签导致当前节点的Diff出现问题,导致报错"没有这个Node"。
解决方案:
- 尽量使用相同类型的包裹元素(比如始终使用
<span>
或者<div>
等)。 - 纯文本节点使用DOM元素
这是一个内部机制的问题,也是我们应该避免的。
文本节点和DOM节点
比如现在我要创建一个<div>Hello, world!</div>
的JSX,我要做的操作如下:
var newDiv = document.createElement('div');
// 创建一个新的文本节点
var newText = document.createTextNode('Hello, world!');
newDiv.appendChild(newText);
document.body.appendChild(newDiv);
-
渲染纯文本节点:
当React遇到文本内容时,例如JSX里包含字符串:<div>Hello, world!</div>
,React会对里面的字符串创建一个文本类型的Fiber节点。这个文本节点不需要创建DOM元素标签,而是直接在父DOM节点内创建文本内容。 -
渲染div节点:
当遇到<div>
这样的HTML元素时,React会创建对应的Fiber节点,并最终通过document.createElement
创建一个DOM元素,并将其插入到DOM树中。
如果一个div节点变化成了纯文本节点,React的Diff在发现节点类型有变化时,会进行如下处理:
- 删除旧的div节点(包括其所有的子节点)。
- 创建并插入新的纯文本节点。