在React应用中,当你尝试使用dangerouslySetInnerHTML来渲染富文本内容时,一个潜在的巨大性能陷阱正等待着你。
性能瓶颈分析
1. 不可避免的“全量DOM更新”
dangerouslySetInnerHTML的工作原理,实际上是绕过了React的虚拟DOM协调机制,直接对真实DOM进行innerHTML赋值。这意味着每次内容更新,React都会将整个目标DOM节点及其所有子节点进行全量替换。

这在内容变化频繁且体量较大的场景下,会带来显著的性能开销。一个典型的例子是Markdown编辑器的实时预览功能(如上图所示)。用户的每一次击键,都会导致整个预览区域的DOM被彻底销毁并重新创建。随着文档内容越来越长,这种卡顿感会变得愈发明显。
2. 资源重新加载导致的视觉闪烁
由于每次都是全量更新,DOM节点会被重新创建,所有已加载的资源(如图片)都会被迫重新请求和渲染。

这直接导致了页面中图片等元素的“闪烁”问题,严重影响了用户的浏览体验。
解决方案:实现差量更新
解决问题的核心思路很明确:只对实际发生变化的节点进行更新,复用未变动的DOM。
这与React自身的虚拟DOM Diff思想如出一辙。我们可以借助morphdom这个高效的DOM Diff库来实现这一目标。它的作用是对比新旧两个HTML结构(字符串或节点),并智能地更新真实DOM,最小化操作。
useEffect(() => {
// 获取当前挂载的DOM节点
const mountNode = document.querySelector('#md-preview');
// 使用morphdom进行差量更新,只修改变化的DOM部分
morphdom(mountNode, newHtmlContent, {
onBeforeElUpdated: (fromEl, toEl) => {
// 只有在新旧节点不完全相等时才执行更新,避免不必要的操作
return !fromEl.isEqualNode(toEl);
},
});
}, [newHtmlContent]); // 依赖项为新的HTML内容
// 组件渲染部分
return <div id="md-preview" />;
通过这种方式,未发生变化的图片等元素对应的DOM节点将得到保留,从而彻底解决因全量更新导致的闪烁与卡顿问题。

总结
React的dangerouslySetInnerHTML属性本质是对原生innerHTML的封装,其全量更新的特性在高频、大内容量的富文本渲染场景下会成为性能瓶颈。通过引入morphdom这类库进行DOM的差量对比与更新,我们可以有效复用未变化的节点,大幅减少不必要的DOM操作开销,从而提升应用的响应速度与用户体验。
|