document.createdocumentfragment() 的核心价值是避免频繁重排,将多次布局合并为一次,提升主线程响应能力;它不挂载、不渲染、不可复用,且不支持 queryselector 和 innerhtml。

用 document.createDocumentFragment() 不是为了让 JS 跑得更快,而是为了不让浏览器在你组装节点时反复 layout——它把 100 次潜在重排压成 1 次,主线程才能响应用户输入、滚动和动画。
为什么循环appendChild会卡,而fragment不会
每次对已挂载容器调用 appendChild(),只要父元素有 flex、grid、float 或 position: relative 这类依赖布局的样式,浏览器就可能立刻触发样式计算 + 几何测量。这不是 JS 慢,是浏览器被逼着同步重排。
- Performance 面板里能看到密集的 Layout 任务堆积,滚动掉帧、input 输入延迟就是外显症状
-
document.createDocumentFragment()返回的是不挂载、无ownerDocument、不参与渲染管线的对象——往里面塞 100 个li,全程零 layout、零 paint、零 computedStyle 缓存开销 - 只有最后调用
container.appendChild(frag)那一刻,浏览器才一次性完成整棵子树的布局计算
必须每次新建 fragment,不能复用
document.createDocumentFragment() 插入后自动清空,不可二次使用。复用已插入过的 fragment 是常见误操作,结果是啥也不显示。
- ✅ 正确:
const frag = document.createDocumentFragment()放在数据就绪后第一时间,循环中frag.appendChild(node),最后container.appendChild(frag) - ❌ 错误:
const frag = document.createDocumentFragment()提前定义一次,多次批量插入时反复用它 - ⚠️ 隐患:
frag.cloneNode(true)返回的是空 fragment,不是复制内容;想“复用”逻辑,只能重新建
fragment 不支持 querySelector 和 innerHTML
DocumentFragment 不是轻量 DOM 树,它不支持查询、不支持设 HTML 字符串——这些行为静默失败或抛错,但容易被忽略。
- 不要写
frag.querySelector('button'),它永远返回null;需查找或绑定事件,必须等插入真实 DOM 后再操作 - 不要写
frag.innerHTML = '<div>test</div>',这行代码无效;若需解析 HTML 字符串,用临时容器中转:const temp = document.createElement('div'); temp.innerHTML = htmlString; while (temp.firstChild) frag.appendChild(temp.firstChild); - 若字符串含
<tr>、<code><option></option>等上下文敏感标签,必须先 append 到对应父容器(如tbody),再把该父容器移入 fragment什么时候必须选 fragment 而非 innerHTML
别只比“谁快”,要看“谁可控”。字符串拼接看似快,但代价是隐性且致命的:事件监听器丢失、
input.value重置、dataset清零、XSS 风险暴露。- 你已有现成节点(比如从
<template></template>克隆的li),用innerHTML就得先outerHTML再 parse,纯浪费 - 每个新节点要绑定不同事件处理器(比如带
data-id的按钮),字符串无法表达闭包逻辑 - 内容来自用户输入或富文本编辑器输出,
textContent+DocumentFragment天然防 XSS,不用手写escapeHtml() - 目标环境需兼容 IE9(
frag.append()是 IE10+ 才支持,但frag.appendChild()全版本通吃)
真正卡顿往往不出在 fragment 本身,而出在它外围:比如在构建 fragment 前读取
offsetHeight,或在循环里混用真实 DOM 查询——这些都会提前 flush layout,让 fragment 的“离线性”失效。 - 你已有现成节点(比如从
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











