做网页开发时,经常要往页面里塞一堆内容,比如加载一长串商品、动态生成表格数据。如果一个一个地插DOM节点,页面卡得像老电脑开Photoshop,用户点一下等三秒,体验直接崩了。
为什么不能逐个插入?
每次用 appendChild 或 innerHTML 添加单个元素,浏览器都会触发一次重排(reflow)和重绘(repaint)。插100个节点就可能跑100次渲染流程,资源白白浪费。就像你搬家非要一趟搬一颗螺丝钉,累死还慢。
用 DocumentFragment 批量操作
最推荐的方式是使用 DocumentFragment。它是个虚拟的DOM容器,不会直接显示在页面上。你可以先把所有节点丢进去,拼装好后再一次性插入真实DOM。
const fragment = document.createDocumentFragment();
const items = ['苹果', '香蕉', '橙子', '葡萄'];
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
});
document.querySelector('ul').appendChild(fragment);
这段代码只触发一次页面重绘,速度明显快很多。适合列表、选项、日志这类需要动态填充的场景。
innerHTML 拼接字符串也行,但要小心
如果你不涉及事件绑定,纯展示内容,直接拼HTML字符串再赋值给 innerHTML 也是可行的。
const items = ['苹果', '香蕉', '橙子'];
const htmlString = items.map(name =>
`<li>${name}</li>`
).join('');
document.querySelector('ul').innerHTML = htmlString;
这种方法写起来简单,执行也快,但要注意XSS风险,别把用户输入直接拼进去。
现代框架早帮你搞定了
Vue、React 这些框架内部已经做了批量更新优化。比如React的 useState 多次调用会被合并,Virtual DOM 也会在提交前攒一波操作再刷到页面。但如果你在这些框架里直接操作原生DOM,一样会掉坑里。
所以就算用了框架,了解底层机制还是有必要的。哪天要写个高性能组件,或者调试卡顿问题,这些知识就派上用场了。
实际应用场景
比如你做个后台管理系统,要渲染上千条订单记录。用循环一个个插,页面卡到怀疑人生。换成 DocumentFragment 或字符串拼接,唰一下就出来了,老板路过看你屏幕都以为加了SSD。
再比如聊天页面,一次性来几十条消息,如果每条都单独插入,不仅卡,还会导致滚动位置乱跳。批量处理就能稳稳地把消息堆上去,用户体验顺滑得多。