美文网首页
DOM性能优化

DOM性能优化

作者: 小宇cool | 来源:发表于2020-05-22 15:50 被阅读0次

DOM编程

用JavaScript进行DOM操作的代价很昂贵,它是富Web应用中最常见的性能瓶颈.

文档对象模型DOM是一个独立于语言,用于操作XML和HTML文档的程序接口(API).在浏览器中主要用来和HTML文档打交道.

尽管DOM是个与语言无关的API,它在浏览器中的接口却是用JavaScript实现的.客户端脚本编程大多时候是在底层文档打交道,DOM就成为现在JavaScript编码中的重要部分

浏览器中通常会把DOM和JavaScript单独实现,JavaScript引擎和DOM渲染引擎相互对立.

这对性能来说意味着什么?简单理解,两个相互对立的功能只要通过接口就能彼此连接,就会产生消耗.把DOM和JavaScript各比作一座岛屿,它们之间用收费桥梁连接.ECMAscript每次访问DOM,都会这过座桥,并交纳过桥费,访问的DOM次数越多,费用就越高.因此推荐的做法就是尽可能的减少过桥的次数

DOM的访问与修改

访问DOM元素是有代价的, 修改元素更为昂贵,因为它会导致浏览器重新计算页面的几何变化.

为了让你对DOM编程带来的性能问题有个量化的了解,下面是简单实例:

function renderHtml(){
  for(let count = 0, ; count < 1000;count++ ){
      document.getElementById("box").innerHTML += a;
  }
}

这个函数循环修改页面元素内容.这段代码的问题在于,每次循环迭代,该元素都被访问两次,一次读取innerHTML属性,另一次是重写它.

换一种效率更高的方法,用局部变量储存修改的内容,在循环结束后一次性写入;

function renderHtml2(){
    var content = '';
    for(let count = 0; count < 1000; count++){
        content += 'a';
    }
    document.getElementById("box").innerHTML += content;
}

如果未来要多次修改和访问DOM元素, 更好的方法将访问的元素用一个局部变量或者全局变量缓存起来, 减少访问DOM次数

const box =  document.getElementById("box");//定义一个变量用来存储DOM元素, 方便以后多次访问
function renderHtml2(){
    var content = '';
    for(let count = 0; count < 1000; count++){
        content += 'a';
    }
   box.innerHTML += content;
}

在所有浏览器中,修改后的版本都运行得更快. 访问DOM的次数越多,代码运行的速度就越慢, 因此通用的经验法则就是:减少访问DOM的次数,

把运算尽量留在ECMAscript这一端处理.

innerHTML和DOM方法的对比

多年来,在web开发社区围绕这个问题有着许多讨论,修改页面区域的最佳方案是用非标准但支持良好的innerHTML属性呢?还是只用document.createElement的原生DOM方法? 如果不考虑web标准, 答案是相差无几. 但是,除开最新版WebKit内核(Chrome和Safari)

之外的所有浏览器中,innerHTML会更快一些.

用innerHTML生产一个ul列表

const body = document.body;
let data = ['name','age','sex','prototype','hobby','height']
function renderUl(rednerData){//参数rednerData为生成的li文本内容数据
   let content =['<ul>',, '</ul>'],
        length = rednerData.length,
        liList = '';
   for(let i = 0; i < length; i++){
       liList += '<li>' +rednerData[i]+ '</li>'
   };
    content[1] = liList;
    let ul = content.reduce( (acc,cur) => acc + cur )
    body.innerHTML = ul
}

使用DOM方法生成相同的ul列表

const body = document.body;
let data = ['name','age','sex','prototype','hobby','height']
function renderUl2(rednerData){//参数rednerData为生成的li文本内容数据
    let ui = document.createElement("ul");
   rednerData.forEach( item =>{
      let li = document.createElement("li");
       li.innerText = item;
       ui.append(li)
   })
    body.append(ui)
}

在旧版本的浏览器中,innerHTMl的优势更加明显,但在新版本浏览器中优势不那么明显. 在基于WebKit内核的新版浏览器中恰恰相反; 使用DOM方法略胜一筹,因此, 最终选择哪种方法取决于你的用户经常使用的浏览器,以及你的编码习惯;

如果对于性能没有那么苛刻的要求,一般推荐使用原生的DOM方法,更容易养成好的编码习惯,所以你更应该根据可读性,稳定性,团队习惯,代码风格来综合决定使用哪种方式

HTML集合

HTML集合是包含DOM节点引用的类数组对象.以下方法的返回值就是一个集合.

  • document.getElementByname();
  • document.getElementsByClasssName();
  • document.getElementsByTagName();

下面的属性同样返回HTML集合

documen.images

  • 页面中所有的img元素

document.links

  • 页面中所有的a元素

document.froms

  • 页面中所有的表单元素

document.form[0].elements

  • 页面中第一个表单的所有字段

以上方法和属性的返回值为HTML集合对象,这是个类似数组的列表.他们并不是真正的数组(因为没有push()和slice方法()之类的方法),但它提供了类似数组的length属性,并且可以通过数字索引访问列表中的元素,例如, document.images[0]返回集合中第一个元素.正如DOM标准中所定义的,HTML集以一种 假定实时态,这意味着当底层文档对象更新时,它也会知道更新

事实上,HTML集合一直与文档保持连接,每次你需要最新的信息时,都会重复执行查询的过程.哪怕只是获取集合中的元素个数(即访问集合的length属性), 也是如此,这正是昂贵性能消耗的源头.

昂贵的集合

为了演示集合的实时性,考虑以下的代码片段

//一个意外的死循坏
let allDivs = document.getElementsByTagName("div");
for(let i = 0; i < allDivs.length; i++){
    let div = document.createElement("div");
    document.body.appendChild(div);
}   

这段代码看上去只是简单把页面的div元素数量翻倍.它遍历现有的div元素,每次创建一个新的div并添加到body中.但事实上这是一个死循环,意外循环的退出条件allDivs.length每次迭代时都会增加,它反映的是底层文档的实时当前状态.

像这样遍历HTML集合可能会导致逻辑错误,而且也很慢,因为每次迭代都会执行查询操作.

通常来说在循环的条件控制语句中读取数组的length属性是不推荐的做法.而且读取一个集合的length比读取普通数组的length属性要慢很多,因为每次都要重新查询.

上面的代码可以改下成下面的代码, 把集合的length属性用一个局部变量保存起来,避免重复的读取集合的length属性,导致死循坏

在每次迭代过程中.读取元素的集合的length属性会引发集合进行更新,这是所有浏览器中都有的明显的性能问题,优化方法很简单吗把集合的长度缓存到一个局部变量,然后在循环退出的条件语句中使用该变量

let allDivs = document.getElementsByTagName("div"),
    length = allDivs.length;
for(let i = 0; i <length; i++){
    let div = document.createElement("div");
    document.body.appendChild(div);
} 

在相同的内容和数量上,遍历应该数组的速度明显快于遍历一个HTML集合.

考虑一个API把一个HTML集合拷贝成一个普通的数组.

let allDivs = document.getElementsByTagName("div"),
    allDivsToArr = Array.form(allDivs);// es6新api把类数组转为真正的数组
for(let i = 0,length = allDivs.length;; i <length; i++){
    let div = document.createElement("div");
    document.body.appendChild(div);
} 

很多情况下如果只需要遍历一个相对较小的集合,那么缓存length就够了.但是由于变遍历数组比遍历集合快,因此如果将集合元素拷贝到数字中,那么访问它的属性就更快.请记住,这会因额为的步骤带来消耗,而且会多遍历一遍集合,因此应当在评估特定的条件下评估使用数组拷贝是否有帮助.

访问集合元素中使用局部变量

一般来说,对于任何类型的DOM访问,需要多次访问同一个DOM属性或方法或方法需要多次访问,最好使用一个局部变量缓存此成员.当遍历一个集合时,第一优先原则就是把集合缓存到局部变量中, 并把length属性缓存在循坏外部,使用局部变量替代这些需要多次读取的元素

看下面的例子,在循坏体中读取元素的三个属性.最慢的版本每次都要读取全局的document,优化的版本缓存了一个集合的引用, 最快的版本把当前集合元素缓存到一个变量,这三个版本都缓存了集合的length属性.

//较慢
function collectionColbal(){
    let coll = document.getElementsByTagName('div'),
        len = coll.length,
        name = '';
   for(let count = 0; count < len; count++){
       name =  document.getElementsByTagName('div')[count].nodeName;
       name = document.getElementsByTagName('div')[count].nodeType;
       name = document.getElementsByTagName('div')[count].tagName;
   }
    return name;
}
//较快
function collectionColbal(){
    let coll = document.getElementsByTagName('div'),
        len = coll.length,
        name = '';
   for(let count = 0; count < len; count++){
       name =  coll[count].nodeName;
       name = coll[count].nodeType;
       name = coll[count].tagName;
   }
    return name;
}
//最快
function collectionColbal(){
    let coll = document.getElementsByTagName('div'),
        len = coll.length,
        name = '',
        el = null;
   for(let count = 0; count < len; count++){
       el = coll[count]//把集合元素缓存到一个变量
       name =  el.nodeName;
       name = el.nodeType;
       name = el.tagName;
   }
    return name;
}

在遍历DOM读取属性时,使用局部变量存储集合引用和集合元素可以带来显著的性能提升.

相关文章

  • 性能、打包题目

    前端性能优化 页面DOM节点太多,会出现什么问题?如何优化? DOM太多会造成页面加载卡顿, 操作DOM节点 在外...

  • DOM性能优化

    背景:DOM操作占用CPU比较多,导致浏览器重新渲染,耗时,所以避免频繁的DOM操作 1.对DOM查询做缓存 2....

  • DOM性能优化

    DOM编程 用JavaScript进行DOM操作的代价很昂贵,它是富Web应用中最常见的性能瓶颈. 文档对象模型D...

  • React 组件性能优化

    React 组件性能优化最佳实践 React 组件性能优化的核心是减少渲染真实 DOM 节点的频率,减少 Virt...

  • 前端性能优化原理与实践(三)

    摘自前端性能优化原理与实践 DOM 优化原理与基本实践 JS是很快的,在 JS中修改DOM对象也是很快的。在JS的...

  • 公开课二、浏览器渲染原理之DOM的重绘和回流 ------ 20

    1、定义: 2、基于DOM的重绘和回流之前端性能优化:

  • 如何提高网页性能?

    1.用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能 2.少用全局变量 3.图...

  • 高性能Java script(DOM操作)

    三、DOM操作的优化 a.Ecama 和 DOM 是两座岛屿 在浏览器中,实现DOM操作是很耗性能的事情 应该做的...

  • 优化方法

    用户体验优化 图片占位 性能优化 减少标签嵌套 减少dom操作 编写动画使用js内置的函数 使用cdn 使用图片压...

  • 移动web相关(二)

    首屏优化 前端性能 我们所说的前端性能一般包括:加载性能;渲染速度;用户交互响应速度;动画流畅性;DOM操作无闪动...

网友评论

      本文标题:DOM性能优化

      本文链接:https://www.haomeiwen.com/subject/dvsxahtx.html