前端性能优化--DOM篇

前言

DOM(Document Object Model)是一个与语言无关的、用来操作XMl和HTML文档的应用程序接口。在浏览器环境下,我们是通过Javascript来实现对DOM的操作。而针对DOM进行频繁操作会严重影响前端的体验和性能,下面我们就简单谈一下如何针对这种情况进行一些优化。

浏览器工作原理

先通过一张图来看一下浏览器是如何进行页面画渲染的:

浏览器工作流程

首先通过HTML文件生成DOM Tree和CSS规则生成CSS Rule Tree,然后Javascript可以通过DOM API和CSSOM API对生成后的DOM Tree和CSS Rule Tree进行需要的操作。接下来CSS Rule Tree中的规则应用到DOM Tree上,构建出Rendering Tree。最后调用操作系统Native GUI的API绘制。

当Rendering Tree中的元素尺寸大小,布局位置,显示隐藏等改变时,需要对Rending Tree进行重新构建,这个过程就称为回流。回流完成后浏览器会重新绘制受影响的部分。回流必引起重绘,而重绘不一定引起回流。

天生就慢的DOM如何优化

实际上Javascipt的执行速度虽然比不上C、C++等预编译语言,但是和访问、操作DOM�比起来那还是要快很多的。当你的Rendering Tree足够大时,每次使用Javascript调用DOM API去访问一个DOM元素,都会执行一个遍历过程,而当你又对获取的元素进行了操作,这就又会触发回流和重绘,这些都会大量消耗性能,很容易造成页面闪烁,卡顿等,影响用户体验。所以我们沿着这个思路就能找到要优化的点。

1. 使用尽可能少的DOM元素来构建页面

页面中过多的DOM元素,会使定位元素变得缓慢。当触发回流和重绘操作时会消耗更多的性能。还有就是在后期的维护上也会增加不必要的复杂性。如果页面真的很复杂,精简DOM不可行,可以用局部变量缓存获取过的DOM元素:

function cacheDOM() {
  // 缓存DOM节点,避免在循环中不断重新获取
  let div = document.getElementsByTagName('div'), 
  let len = div.length;
  for(let i = 0; i < len; i++) {
    console.log(div.nodeType);
  }  
}

还可以使用一些高效的API,优先使用document.getElementById,而document.querySelectordocument.querySelectorAll在性能上和getElementsBy*API相比较慢。

2. 减少回流和重绘

在操作DOM元素时,除了获取过程,还有对元素的外观,布局等进行的修改。触发回流的操作有以下类型:

  1. 添加或者删除可见的DOM元素

  2. 改变元素的位置

  3. 元素的内边距、外边距、边框、宽度、高度等尺寸的改变

  4. 元素文本内容和图片大小的改变导致高度和宽度的重新计算

  5. 浏览器窗口尺寸的改变

  6. 页面初始化渲染

     var s = document.body.style;
     s.padding = "2px"; // 回流+重绘
     s.border = "1px solid red"; // 再一次 回流+重绘
     s.color = "blue"; // 再一次重绘
     s.backgroundColor = "#ccc"; // 再一次 重绘
     s.fontSize = "14px"; // 再一次 回流+重绘
     // 添加node,再一次 回流+重绘
     document.body.appendChild(document.createTextNode('abc!'));
    

通过上面的代码可以看出回流和重绘频繁发生。

而在实际的浏览器渲染过程中,会对上述过程做优化。连续的回流和重绘会被放入一个队列中,当任务积累到一定数量或者一定的时间间隔,就会刷新队列。减少了多次渲染的性能消耗。但是有些操作,浏览器为了返回正确的值,会忽略优化,立刻执行回流和重绘操作,比如:offsetTopscorllTopclientTopwidth等。
如何减少DOM的回流和重绘呢?我们可以从以下几个方面入手:

a. 在动态修改CSS样式时,不要逐条设置,可以使用类名

    element.style.fontWeight = 'bold' ;
    element.style.marginLeft= '30px' ;
    element.style.marginRight = '30px' ;

    // 可以优化为如下代码,可以将多次回流减少到一次
    element.className = 'selectedAnchor' ;

b. 对DOM的操作,留在Javascript内部,然后一次写回DOM,即实现离线处理

第一种,可以使用DocumentFragment的机制:DocumentFragment接口表示没有父级的最小文档对象,由于文档片段不是实际DOM结构的一部分,它是一个虚拟的dom节点,存在于内存中,所以对片段所做的更改不会影响文档,导致回流,影响性能。

  function CreateFragments(){
    var fragment = document.createDocumentFragment();
    for(var i = 0;i < 10000;i++){
      var tmpNode = document.createElement("div");
      tmpNode.innerHTML = "test" + i + "<br />";
      fragment.appendChild(tmpNode);
    }
    document.body.appendChild(fragment);
  }

第二种,使用使用cloneNode在外部更新节点然后再通过replace与原始节点互换,也只触发一次回流重绘。

  var orig = document.getElementById('container');
  var clone = orig.cloneNode(true);
  var list = ['foo', 'bar', 'baz'];
  var content;
  for (var i = 0; i < list.length; i++) {
    content = document.createTextNode(list[i]);
    clone.appendChild(content);
  }
  orig.parentNode.replaceChild(clone, orig);

还有一种方法,比较简单,将要操作的DOM设置为dispaly:none,操作完成后再设置为display:block,这样只触发了两次回流。

c. 对于动画元素,尽可能使其脱离文档流,使用fixedabsolute定位。在进行动画制作时,如果能用opcity和transform实现,就不要使用其他属性。这两个属性只会触发composite,而不会触发回流。

总结

本文从浏览器的工作原理触发,简单说明了一下DOM慢的原因,然后给出了优化DOM操作的几个建议。主要目的就是为了精简DOM结构、减少DOM的回流和重绘。如果要对页面的性能瓶颈进行详细的分析,可以借助Chrome开发工具中的Performance面板。找到具体的原因,针对性的提出优化方案。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容