document.createDocumentFragment能将N次重排压为1次,因其作为离线容器不挂载DOM树,所有节点在内存中组装不触发layout/paint,仅最后appendChild(frag)时迁移子节点并执行单次布局;复用已插入fragment会失效,且不支持querySelector和innerHTML。

document.createDocumentFragment 为什么能压住重排次数
它不是更快的 appendChild,而是绕开了渲染管线——fragment 本身不挂载到 DOM 树,所有子节点在内存中组装,浏览器根本不会触发 layout 或 paint。只有最后调用 container.appendChild(frag) 那一刻,fragment 的子节点才被“迁移”进真实 DOM,此时才做一次 layout。
常见错误是复用已插入过的 fragment:它插入后自动清空,再 append 就什么也不发生;还有人试图对 fragment 调用 querySelector 或设 innerHTML,这两者都不支持,会直接报错或静默失败。
- 必须每次新建:
const frag = document.createDocumentFragment() - 所有节点先
frag.appendChild(node)或frag.append(node1, node2) - 仅最后一步调用
realContainer.appendChild(frag),且只能调用一次
和 innerHTML 拼接比,fragment 在哪些场景不可替代
字符串拼接快,但丢东西:事件监听器、dataset、表单 value、动态 class 切换逻辑全没了。你已有带 data-id="123" 的按钮,用 innerHTML 就得先 outerHTML 再 parse,纯浪费。
更关键的是安全:用户输入或富文本内容直接塞进 innerHTML 是 XSS 温床;而用 textContent + fragment 天然防注入,不用手写 escapeHtml()。
立即学习“前端免费学习笔记(深入)”;
- 需要为每个节点绑定不同事件(比如每行一个 delete handler)→ 字符串无法表达分支逻辑
- 目标环境要兼容 IE9 →
append()方法 IE10+ 才支持,但appendChild+ fragment 全版本通吃 - 节点来自模板克隆(
template.content.cloneNode(true))→ 直接 append 进 fragment,结构/属性/事件全保留
fragment 解决不了的三个高频卡点
它只管“插入”,不管“更新”和“调度”。很多团队用了 fragment 还卡,是因为漏掉了这三件事:
- 在 fragment 插入前读取任何布局信息(如
el.offsetHeight),会强制浏览器提前 flush,把离线优势全抵消 - 没做节点复用池,每次 render 都
document.createElement('li')→ 触发内存分配 + 样式树挂载 + layout/paint 全流程,GC 压力飙升 - 事件监听器没在回收前解绑 →
removeChild不等于释放,IE11 和旧 Edge 中node.remove()不清理事件,必须显式removeEventListener
fragment 和 DOM 复用池怎么配合用
fragment 负责“批量插入”,复用池负责“节点生命周期管理”。两者是上下游关系,不是二选一。
典型组合:用 domPoolFactory 提前创建一批 div,render 时从池里 pop() 出来,设置 textContent、绑定事件、加 dataset,然后 frag.appendChild(node);render 完再把旧节点 textContent = ''、innerHTML = ''、dataset.index = '' 清空后 push() 回池。
注意:不要在 createFn 里绑定事件——那会让所有复用节点共享同一个 handler;回收前必须解绑,否则闭包引用会锁住整个作用域。



















