document.createdocumentfragment 能减少重排,因为它作为离线容器,操作时不触发样式计算和布局,仅最后一次性插入真实 dom 时引发一次重排。

document.createDocumentFragment 为什么能减少重排
浏览器在插入多个 DOM 节点时,如果逐个 appendChild 到真实父元素,每次插入都可能触发样式计算和布局(即重排),尤其当父元素已挂载、有复杂 CSS 或存在浮动/定位时。而 DocumentFragment 是一个轻量级的“离线”容器,它不绑定到文档树,对它的操作不会触发任何重排或重绘。只有最后一次性 appendChild 它到真实 DOM 时,才产生一次布局影响。
正确创建并填充 fragment 的典型写法
关键不是“用不用”,而是“怎么填”——避免在 fragment 上反复调用 appendChild 同时又误触真实 DOM。常见错误是把 fragment 当成普通父节点去循环 appendChild 子节点,却忘了子节点本身可能还带着未脱钩的父引用或事件监听器。
- 用
document.createDocumentFragment()创建空 fragment - 用
document.createElement()创建新节点,再fragment.appendChild(node)—— 不要复用已挂载节点(否则会自动从原位置移除) - 若需批量克隆已有节点,用
node.cloneNode(true),但注意克隆后仍需手动绑定事件(原事件不会继承) - 避免在循环中反复读取
fragment.childNodes.length等触发 layout 的属性(虽然 fragment 无 layout,但习惯性读取易迁移到真实 DOM 场景)
对比:直接插入 vs fragment 批量插入的性能差异
差异在真实 DOM 深度和样式复杂度高时才明显。比如向一个带 flex-wrap、多层 transform 和 will-change 的容器里插入 50 个 div:
const container = document.getElementById('list');
// ❌ 直接插入:50 次潜在重排
for (let i = 0; i
<p>注意:<code>frag.appendChild(el)</code> 返回的是 <code>el</code> 本身,不是 fragment,别误以为要链式调用。</p>
<h3>容易被忽略的边界情况</h3>
<p>fragment 不是万能加速器,以下场景它帮不上忙,甚至引入误解:</p>
-
DocumentFragment不支持querySelector或getBoundingClientRect()—— 它没渲染,也没尺寸 - 如果插入前需要测量某个子节点高度(比如做虚拟滚动预估),必须先临时 append 到
body或隐藏容器,测完再移走,不能依赖 fragment - 使用
innerHTML设置 fragment 内容(如frag.innerHTML = htmlStr)是无效的 —— fragment 没innerHTMLsetter,得用DOMParser或先塞进临时div再moveChild - 现代框架(React/Vue)内部已做类似优化,手动混用 fragment 可能破坏其 diff 逻辑,纯 DOM 场景才推荐
真正省下的不是代码行数,是浏览器强制同步 layout 的次数;而人最容易忘掉的,是 fragment 里塞的节点一旦被 append 到真实 DOM,就再也不是“离线”的了。










