当前位置:首页 > 技术分析 > 正文内容

Dom节点优化方案

ruisui883周前 (04-08)技术分析16

//从PPT走向丝滑。。。。。

DOM操作对性能影响最大是因为它导致了浏览器的重绘和回流,我们都知道页面UI的更改都是通过DOM操作实现的,DOM虽然提供了许多api方便我们操作dom,但DOM操作的代价很高,页面前端代码的性能瓶颈也大多集中在DOM操作上,所以前端性能优化的一个主要的关注点就是DOM操作的优化。

浏览器渲染机制:


浏览器渲染页面

  1. 浏览器解析 HTML 文档的源码,然后构造出一个 DOM 树,遇
  2. 到样式就异步计算。
  3. 异步计算好的样式与dom树合成,构建 render 树。
  4. 进行布局(layout) render 树。
  5. 进行绘制(painting) render 树。
  6. 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

对静态集合的操作不会引起对文档的重新查询,相比于动态集合更加优化。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/3373.html

分享给朋友:

“Dom节点优化方案” 的相关文章

5个看起来像 MacOS 的 Linux 发行版,赶紧收藏!

既想使用 Linux,又想同时使用 Mac ?那么你可以尝试这些 Linux 发行版,你肯定会觉得自己在用 Mac 系统。1. Elementry OSElementry OS 是看起来像 Mac OS 的最好的 Linux 发行版。和 Mac 一样,这个操作系统也是为了保护隐私而设计的。因此你会得...

手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】

组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。那么组件间如何通信,也就成为了vue中重点知识了。这篇文章将会通过props、$ref和 $emit 这几个知识点,来讲解如何实现父子组件间通信。转载链接:https://www.jia...

「2022」打算跳槽涨薪,必问面试题及答案——VUE篇

1、为什么选择VUE,解决了什么问题?vue.js 正如官网所说的,是一套构建用户界面的渐进式框架。与其它重量级框架不同的是,vue 被设计为可以自底向上逐层应用。vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另外一方面,当与现代化工具链以及各种支持类库结合使用时,vu...

Gitlab概览

Gitlab是开源的基于Git的仓库管理系统,也可以管理软件开发的整个生命周期,是项目管理和代码托管平台,支撑着整个DevOps的生命周期。Gitlab很容易选为GitHub,作为公司私有库管理的工具。我们可以用Gitlab Workflow来协同整个团队的软件开发管理过程。软件开发阶段Gitlab...

「Git迁移」三行命令迁移Git包含提交历史,分支,tag标签等信息

问题描述:公司需要将一个git远程服务器的全部已有项目迁移到一台新服务器的Gitlab中,其中需要包含全部的提交纪录,已有的全部分支与全部打tag标签,目前此工作已全部迁移完毕,特此记录一下操作步骤环境描述:1. 要迁移的远程Git:Gitblit2. 迁移目的Git:Gitlab3. 暂存代码的P...

HTML5学习笔记三:HTML5语法规则

1.标签要小写2.属性值可加可不加””或”3.可以省略某些标签 html body head tbody4.可以省略某些结束标签 tr td li例:显示效果:5.单标签不用加结束标签img input6.废除的标签font center big7.新添加的标签将在下一HTML5学习笔记中重点阐述。...