直接插入2000+节点卡顿主因是频繁layout重排而非js慢;应使用documentfragment分片插入(每帧50–100节点),配合requestanimationframe,避免同步读取布局、contenteditable陷阱及table结构兼容问题。

为什么直接插入2000+节点会卡帧
不是JS执行慢,是浏览器在已挂载容器里逐个 appendChild 时,反复触发 layout 重排。尤其当父元素含 flex、grid、box-shadow 或字体渲染逻辑时,每次插入都可能拉主线程去算布局——Chrome DevTools 的 Layout 面板里能看到密集的“Layout”事件。DOM 节点超 2000 后,帧率明显掉到 30fps 以下,用户感知就是卡顿。
用 DocumentFragment + requestAnimationFrame 分片插入
单靠 document.createDocumentFragment() 只解决“一次重排”,但若一次性塞进 5000 个节点,JS 主线程仍会被长时间阻塞,页面失去响应。必须配合分帧:把大列表切成小块,每帧只处理一块。
- 每块控制在 50–100 节点内(实测 Chrome 下 80 是平衡点)
- 用
requestAnimationFrame触发每帧插入,避免阻塞输入和滚动 - fragment 必须每次新建:
const frag = document.createDocumentFragment(),不能复用(插入后自动清空) - 节点必须用
document.createElement()显式创建,不能对 fragment 赋值innerHTML(静默失败)
contenteditable 容器里插入要绕开三个坑
编辑器类场景(如富文本、代码块渲染)目标常是 <div contenteditable="true">,它对插入更敏感:<ul>
<li>别在 fragment 中混用 <code>insertAdjacentHTML 或 innerHTML —— fragment 不支持这些方法
getSelection() 或 offsetHeight 会强制同步 layout,抵消优化效果;需延后到下一帧:requestAnimationFrame(() => { /* 读取光标/尺寸 */ })
MutationObserver 监听变化,注意 fragment 插入只触发一次 childList 变更,不是每个子节点单独触发table 结构下 fragment 插入要额外包装
直接把 <tr> append 到 fragment 再插进 <code><table>,旧版浏览器(IE11、部分 Safari)会静默修正结构,导致 tbody 缺失或 tr 被丢弃。<ul>
<li>正确做法:先用 <code>document.createElement('tbody') 包一层,再把所有 tr append 进去
tbody 插入目标 table,而不是 fragmenttable.insertRow() + row.insertCell(),兼容性更好但写法略冗长分片插入不是“切得越细越好”,帧间调度本身有开销;50 节点/帧是多数场景下的甜点值,但如果你的节点带大量 inline style 或复杂 dataset,可能得压到 30。真正容易被忽略的是:fragment 插入后节点所有权转移,但事件监听器依然有效——这点常被误以为要重新绑定。











