网站首页 > 基础教程 正文
//从PPT走向丝滑。。。。。
DOM操作对性能影响最大是因为它导致了浏览器的重绘和回流,我们都知道页面UI的更改都是通过DOM操作实现的,DOM虽然提供了许多api方便我们操作dom,但DOM操作的代价很高,页面前端代码的性能瓶颈也大多集中在DOM操作上,所以前端性能优化的一个主要的关注点就是DOM操作的优化。
浏览器渲染机制:
浏览器渲染页面
- 浏览器解析 HTML 文档的源码,然后构造出一个 DOM 树,遇
- 到样式就异步计算。
- 异步计算好的样式与dom树合成,构建 render 树。
- 进行布局(layout) render 树。
- 进行绘制(painting) render 树。
- DOM树与render树的区别在于:样式为display:none;的节点会在DOM树中而不在渲染树中。浏览器绘制了之后便开始解析js文件,根据js来确定是否重绘和重排。
回流·重绘
页面更改发生的操作:
回流:浏览器引擎发现render树某个节点发生了变化影响了布局,需要倒回去重新渲染,我们称这个回退的过程叫 回流。回流会从这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置。
重绘:改变某个元素的背景色、文字颜色、边框颜色等等不影响页面dom布局的操作。
js是单线程的,重绘和重排会阻塞用户的操作以及影响网页的性能
优化:
减少回流重绘次数
1、改变dom多个样式,使用class,而非style,减少多次触发回流重绘
举例:改变dom元素宽高
var dom = document.getElementById('box')
dom.style.width = '300px'
dom.style.height = '300px'
//访问了三次dom,触发了两次回流和两次重绘
优化后:
.change {
width: 300px;
height: 300px;
}
document.getElementById('div').className = 'change'
//只触发一次
2、列表类型批量修改,脱离文档流再恢复,利用样式为display:none;的节点会在DOM树中而不在渲染树中不会引起重绘回流。
如果要在一个dom集合中,给每个dom子节点加一个class,我们可以遍历给每一个节点都加上class,这样就触发了多次的重绘和回流
/* //需要加入的样式
.change {
width: 300px;
height: 300px;
}
*/
var ul = document.getElementsByTagName('ul')
var lis = document.getElementsByTagName('li')
ul.style.display = 'none'
for(var i = 0; i < lis.length; i++) {
lis[i].className = 'change';
}
ul.style.display = 'block'
3、DocumentFragment
虚拟DOM其实就是一个对象,js提供了reateDocumentFragment()方法用于创建一个空的虚拟节点对象,DocumentFragment节点不属于文档树,当需要添加多个dom元素时,如果先将这些元素添加到DocumentFragment中,然后再将DocumentFragment对象添加到渲染树上,会减少页面渲染dom的次数,效率会明显提升。
var frag = document.createDocumentFragment() //创建一个虚拟节点对象
for(var i = 0; i < 10; i++) {
var li = document.createElement("li")
li.innerHTML = '我是第' + i + 1 + '个元素'
frag.appendChild(li) //将li元素加到虚拟节点对象上
}
ul.appendChild(frag) //将虚拟节点对象加到ul上
其它
1、事件委托,利用浏览器事件,冒泡捕获减少页面事件绑定,我们可以指定一个事件处理程序就可以管理某一类型的所有事件。事件函数过多会占用大量内存,而且绑定事件的DOM元素越多会增加访问dom的次数,对页面的交互就绪时间也会有延迟。
// 事件委托前
var lis = document.getElementsByTagName('li')
for(var i = 0; i < lis.length; i++) {
lis[i].onclick = function() {
console.log(this.innerHTML)
}
}
// 利用浏览器事件通过父元素委托事件给子元素
var ul = document.getElementsByTagName('ul')
ul.onclick = function(event) {
//也可以做判断给指定的子元素绑定事件
console.log(event.target.innerHTML)
};
2、在循环中的优化减少操作dom次数
//例子1:减少在计算过程中操作dom
// 优化前,访问了好多次dom,这些都是细节问题,有经验的绕过,小白平常多注意就行
for(var i = 0; i < 10; i++) {
document.getElementById('el').innerHTML += '1'
}
// 优化后
var str = ''
for(var i = 0; i < 10; i++) {
str += '1'
}
document.getElementById('el').innerHTML = str
//例子2, 缓存节点长度
// 优化前
var lis = document.getElementsByTagName('li')
for(var i = 0; i < lis.length; i++) {
// do something...
}
// 优化后,将length的值缓存起来就不会每次都去查询length的值
var lis = document.getElementsByTagName('li')
for(var i = 0, len = lis.length; i < len; i++) {
// do something...
}
这样看获取你体验不到缓存节点长度的作用,请看下面的例子
//不缓存
var divs = document.getElementsByTagName("div"), i, div;
for( i=0; i<divs.length; i++ ){
div = document.createElement("div");
document.body.appendChild("div");
}
造成死循环,每次执行for循环都会动态获取divs的长度,而我们每次进入循环都增加了一个DOM(div),divs的长度也+1.
//缓存
var divs = document.getElementsByTagName("div"), i, div,len;
for( i=0;len=divs.length;i<len; i++ ){
div = document.createElement("div");
document.body.appendChild("div");
}
//使用变量保存divs的长度。
3、选择器区别
获取元素最常见的有两种方法,getElementsByXXX()和queryselectorAll(),这两种选择器区别是很大的,前者是获取动态集合,后者是获取静态集合
// 假设一开始有2个li
var lis = document.getElementsByTagName('li') // 动态集合
var ul = document.getElementsByTagName('ul')[0]
for(var i = 0; i < 3; i++) {
console.log(lis.length)
var newLi = document.createElement('li')
ul.appendChild(newLi)
}
// 输出结果:2, 3, 4
// 优化后
var lis = document.querySelectorAll('li') // 静态集合
var ul = document.getElementsByTagName('ul')[0]
for(var i = 0; i < 3; i++) {
console.log(lis.length)
var newLi = document.createElement('li')
ul.appendChild(newLi)
}
// 输出结果:2, 2, 2
对静态集合的操作不会引起对文档的重新查询,相比于动态集合更加优化。
- 上一篇: 自动化测试工程师的学习方法和逻辑
- 下一篇: 纯JS手写轮播图(代码逻辑清晰,通俗易懂)
猜你喜欢
- 2024-10-12 纯JS手写轮播图(代码逻辑清晰,通俗易懂)
- 2024-10-12 谷歌浏览器任意文件访问漏洞(CVE-2023-4357)复现
- 2024-10-12 自动化测试工程师的学习方法和逻辑
- 2024-10-12 Web Components 系列(八)——自定义组件的样式设置
- 2024-10-12 JavaScript,自制弹窗(练习) js弹出自定义窗口
- 2024-10-12 掌握 onmouseover 事件:打造动态 HTML 体验
- 2024-10-12 Vue 自定义指令详解-按钮级权限示例(干货)
- 2024-10-12 windows本地程序调用与selenium的简单应用
- 2024-10-12 中高级渗透测试员都必须掌握的Web安全测试知识库,速收藏!
- 2024-10-12 碎片时间学编程「173]:显示所有指定的元素
- 最近发表
-
- 自动脚本操作流程(自动脚本怎么做)
- Ubuntu系统存在严重漏洞,几句命令轻松攻破root权限
- redis实现分布式锁(redis实现分布式锁的原理)
- Linux系统移植之—uboot移植,你们要的uboot终于来了,堪称精品
- powershell 后台运行命令(如何在powershell运行程序)
- 生产环境H200部署DeepSeek 671B 满血版实战(三):SGLang 安装详解
- OpenWrt 小白常用命令大全(openwrt操作命令)
- Linux-shell脚本多线程实现(shell 多线程并发执行)
- Thread.sleep(0):线程休眠0秒有什么意义?
- 40 个简单又有效的 Linux Shell 脚本示例
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)