documentfragment 是尚未插入 dom 的离线节点容器,不参与渲染、无 ownerdocument、不触发 layout;插入后自动清空,不可复用,且不支持 innerhtml;读取布局属性会破坏其离线性,应插入后再获取尺寸。

DocumentFragment 不是“快一点的 DOM”,而是“还没进 DOM 的 DOM”
它根本不是真实 DOM 树的一部分,没有 ownerDocument、不参与样式计算、不触发 layout、不占渲染内存。你调用 document.createDocumentFragment() 得到的只是一个空壳对象,Chrome 里大概只占 80 字节。所有你往里面 appendChild() 的节点,只是被临时持有引用——它们的内存早已由 document.createElement() 分配好了,fragment 不复制、不解析、不重建。
插入后 fragment 自动清空,复用等于踩坑
这是最容易误判的一点:很多人把 frag 当成可重置的缓存容器,循环里反复 frag.appendChild(node) 然后 container.appendChild(frag),结果第二轮 frag.childNodes.length 是 0,节点全没了,但代码还在继续塞——看似没报错,实则什么都没插入。
- 每次批量操作前必须新建:
const frag = document.createDocumentFragment() - 插入后
frag.firstChild变为null,再读取或遍历会失效 - 不能靠
frag.innerHTML = ''清空(它根本不支持innerHTML)
别在 fragment 里读 offsetHeight 或 getBoundingClientRect
哪怕你只是读取页面上某个已挂载元素的 offsetHeight,也会强制浏览器 flush 掉所有 pending layout,让 fragment 的“离线性”彻底失效——原本想压成 1 次重排,结果变成 N 次同步 layout。
- 布局读取必须放在
container.appendChild(frag)之后 - 如果真需要滚动位置或尺寸,先记下目标元素引用,等 fragment 插入完成再查
- 在线沙箱环境(如 CodePen)中这种问题更敏感,卡顿会立刻暴露
innerHTML 和 fragment 不是性能高低问题,是控制权归属问题
当你要插入的是纯字符串且无交互,innerHTML 更快;但只要涉及事件绑定、表单状态保留、dataset、XSS 防御或节点复用,innerHTML 就是自废武功。
-
innerHTML +=会全量重建子树,复杂度从 O(n) 升至 O(n²) - 用户输入内容直接拼进
innerHTML,可能执行内联 script,且丢失已有监听器 - 含
<tr> 或 <code><li>的 HTML 字符串,必须借document.createElement('tbody')等上下文容器解析,再移入 fragment真正难的不是创建 fragment,而是后续——节点插进去了,谁负责回收?闭包有没有强引大数据?WeakMap 用对没?这些才是百万级列表里内存持续上涨的根因。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











