documentFragment 避免浏览器reflow提高性能

本文介绍如何通过使用documentFragment来减少DOM操作引起的reflow,优化页面性能。


在脚本化文档时,应该避免频繁地引起浏览器reflow(关于reflow的详细讲解请看前面的文章里的一个视频

减少reflow,优化性能的途径很灵活,总的一句就是把多次reflow攒在一起一次性进行。

如利用innerHTML、一次性改变样式。

今天想说一下documentFragment。

documentFragment相当于一个临时容器,我们把添加的dom元素在这个documentFragment上加上(此时并没有加入到document中,也就不会引起浏览器reflow),当所有的dom元素操作完后,再一次性地把documentFragment对象加入到document中,只发生一次reflow。

而说documentFragment对象是“临时容器”再适合不过了,因为它不会被加入到document对象中,当它把里面的内容加入到document对象里时,自已就“功成身退”了。

<ul id="menu">目录</p>

<script type="text/javascript">
	function insert(elem){
		var df = document.createDocumentFragment();
		for(var i=0;i<3;i++){
			var tempLi = document.createElement("li");
			li.innerHTML = "子目录"+i;
			df.appendChlid(tempLi); //这里浏览器文档没有结构还没改变,不会发生reflow.
		}
		elem.appendChlid(df);	//df片段其内容加入到elem中,但df本身并不在document中。发生一次reflow
	}
        var menu = document.getElementById("menu");
        insert(menu);
 </script>

`DocumentFragment` 是一个轻量级的、不在 DOM 树中的 **虚拟 DOM 容器**,它可以临时存储一组 DOM 节点,直到你将它们一次性插入到实际文档中。 ### ✅ 使用 DocumentFragment 创建 1000 个列表项的示例: ```javascript // 1. 创建一个 DocumentFragment const fragment = document.createDocumentFragment(); // 2. 创建 ul 容器 const ul = document.createElement('ul'); // 3. 添加 1000 个 li 到 ul(仍在内存中,未渲染) for (let i = 0; i < 1000; i++) { const li = document.createElement('li'); li.textContent = '项目 ' + i; ul.appendChild(li); } // 4. 将 ul 添加到 fragment fragment.appendChild(ul); // 5. 一次性将 fragment 插入页面(触发一次重排/重绘) document.body.appendChild(fragment); ``` --- ### 🔍 它是如何提升性能的? #### 🌪️ 问题:频繁操作 DOM 的代价 每次调用 `appendChild()`、修改属性或添加样式时,浏览器都可能触发: - **重排(Reflow)**:重新计算布局 - **重绘(Repaint)**:重新绘制像素 如果在循环中直接把每个元素插入页面,会导致 **1000 次重排和重绘**,严重降低性能。 #### ✅ 解决方案:使用 DocumentFragment 所有 DOM 操作都在“离线”状态下进行(即不直接挂在主 DOM 树上),因此: - 不会触发任何重排或重绘 - 所有节点构建完成后,**一次性插入真实 DOM** - 浏览器只需执行一次布局更新,极大提升性能 --- ### 💡 更典型的优化场景:逐个添加元素 ```javascript const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { const li = document.createElement('li'); li.textContent = '项目 ' + i; fragment.appendChild(li); // 所有 li 都加到 fragment 中 } // 假设有一个已存在的 ul document.querySelector('ul').appendChild(fragment); // 此时才触发一次重排! ``` > ⚠️ 注意:一旦 `DocumentFragment` 被插入,它的子节点会被移除并插入目标位置 —— 它只能使用一次。 --- ### ✅ 优点总结: | 特性 | 说明 | |------|------| | **性能高** | 避免多次重排/重绘 | | **DOM 离线操作** | 修改不会影响页面渲染 | | **兼容性好** | 所有现代浏览器及 IE9+ 支持 | | **事件安全** | 可提前绑定事件监听器,插入后依然有效 | --- ### ❗对比其他方法: | 方法 | 是否触发多次重排 | 性能 | 安全性 | |------|------------------|-------|--------| | `createElement` + 直接 `appendChild` 在循环中 | ✅ 是 | ❌ 差 | ✅ 高 | | `innerHTML` 字符串拼接 | ❌ 否(一次性) | ✅ 好 | ❌ 低(XSS 风险) | | `DocumentFragment` + `createElement` | ❌ 否(仅插入时一次) | ✅✅ 最佳 | ✅ 高 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值