用 DocumentFragment 批量构建节点再一次性插入可将100个li的插入从100次重排降至1次,节点数≥10即值得使用;它是离屏容器,操作不触发渲染,但不可赋值innerHTML,不支持querySelector,需注意table结构限制和分帧处理。

直接结论:用 document.createDocumentFragment() 批量构建节点再一次性插入,能把 100 个 li 的插入从潜在 100 次重排压到 1 次——只要节点数 ≥ 10,就值得切。
为什么循环里逐个 appendChild 特别慢
浏览器每次往已挂载的父容器(比如一个 ul)里加一个新节点,都可能触发样式计算、布局(reflow)和重绘(repaint)。尤其当父元素有 flex、浮动、边框阴影或字体渲染逻辑时,这个开销会放大。50 次 appendChild ≠ 50 次轻量操作,而是主线程反复被拉去干重活。
而 DocumentFragment 是离屏容器,不在真实 DOM 树中,对它的任何 appendChild、append 都不触发渲染——纯内存操作。
- 别指望它像虚拟 DOM 那样自动 diff;它就是个“暂存篮子”
- IE9+ 全支持,不用 polyfill
- 插入后自动清空,变量不能复用(再次
appendChild什么也不会发生)
DocumentFragment 怎么填才不出错
核心原则:所有节点必须是通过 document.createElement 创建,或从已有 DOM 中移入;不能给 fragment 赋值 innerHTML,它没这个属性。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
立即学习“前端免费学习笔记(深入)”;
- ✅ 正确:
frag.appendChild(document.createElement('li')) - ❌ 错误:
frag.innerHTML = '<li>xxx</li>'(静默失败) - ❌ 错误:
el.innerHTML += frag(拼出[object DocumentFragment]字符串) - ⚠️ 注意:
table场景下,不能把tr直接 append 到 fragment 后再插进table;得先插进tbody或thead,否则浏览器会静默修正结构
和 innerHTML 拼接比,该选哪个
不是性能绝对谁赢,而是看你要保什么:
- 要快 + 纯静态展示?
innerHTML = arr.map(x => `<li>${x}</li>`).join('')更简短,也更快(尤其结构简单) - 要绑定不同
data-id、预留 click 处理、或后续还要改某个li的textContent?用 fragment,你能拿到每个节点的引用 - 内容来自用户输入?
textContent+ fragment 天然防 XSS;而innerHTML拼接必须手动转义,漏一处就中招 - 要保留已有 input 的 value、滚动位置、事件监听器?
innerHTML会全毁;insertAdjacentHTML('beforeend', ...)可以,但它不走 fragment 路线
真正容易被忽略的点
很多人写了 fragment 却没提速,问题常出在边界上:
- 在 for 循环里反复调用
document.createDocumentFragment()——应该只创建一次,然后一直往里appendChild - 调试时想检查 fragment 里有什么,直接
console.log(frag)看不到子节点;得临时document.body.appendChild(frag)瞅一眼,再body.removeChild移走 - fragment 不支持
querySelector、getElementById——它不是完整文档,只是轻量容器 - 如果列表项超 500 个,单帧构建 fragment 本身也可能卡住;这时得配合
requestAnimationFrame分帧处理,而不是“攒完再插”


















