documentfragment 是离线 dom 容器,不触发重排重绘;先批量操作再整体插入真实 dom,仅引发一次重排,显著提升性能。

document.createDocumentFragment 是浏览器提供的轻量级 DOM 容器,它不直接属于主文档树,因此在其上进行任何增删改操作都不会触发重排(reflow)或重绘(repaint)。将多个 DOM 操作先集中到 DocumentFragment 中,最后一次性插入真实 DOM,是减少重排次数、提升性能的经典做法。
为什么 Fragment 能避免重排?
DocumentFragment 是一个“离线”节点,没有绑定到 document.body 或任何可视元素。浏览器不会为它计算布局、绘制样式或响应尺寸变化。只有当它被 append 到真实 DOM 时,才会作为整体参与一次布局计算——而不是每个子节点都单独触发一次。
对比直接循环插入:
- 直接在 body 上循环
appendChild(div):每插入一个元素,浏览器可能检查布局是否受影响,频繁触发重排(尤其在旧版浏览器或复杂样式下) - 先插入到 fragment,再
body.appendChild(fragment):仅最终这一步引发一次重排
典型批量插入场景示例
比如要动态生成 100 个列表项并添加到 <ul></ul> 中:
// ❌ 低效:每次 appendChild 都可能触发重排
const ul = document.querySelector('ul');
for (let i = 0; i
<p>✅ 推荐写法:</p>
<pre class="brush:php;toolbar:false;">const ul = document.querySelector('ul');
const fragment = document.createDocumentFragment();
for (let i = 0; i
<h3>配合模板或 innerHTML 的注意事项</h3>
<p>DocumentFragment 本身不支持 <code>innerHTML</code>,但你可以结合 <code>template</code> 元素或临时容器来构建结构再移入 fragment:</p>
- 用
<template></template>预定义结构,content.cloneNode(true)后 append 到 fragment - 也可创建临时
div设置innerHTML,再把它的childNodes移入 fragment(注意用while(div.firstChild)或Array.from(div.childNodes)避免 live NodeList 问题) - 不要对 fragment 直接赋值
innerHTML(无效且静默失败)
何时可以不用 Fragment?现代优化提示
现代浏览器(Chrome/Firefox/Safari)对连续的 DOM 插入有一定优化,例如同一父节点下的多次 appendChild 可能被合并处理。但 Fragment 仍是最可靠、语义最清晰、兼容性最好的方案,尤其在以下情况必须用:
- 目标父节点样式含
display: table或flex/grid等复杂布局,易因单次插入扰动布局 - 需在插入前统一设置子节点公共属性(如 class、dataset),避免重复操作真实 DOM
- 跨浏览器兼容性要求高(包括较老版本 Edge 或移动端 WebView)
不复杂但容易忽略:Fragment 不是银弹,但它让“批量更新”这件事变得明确、可控、可读。只要涉及 5 个以上节点的动态插入/移动,就值得用它封装一下。










