浏览器渲染机制与性能优化

  详读了很多文章,最终对比总结出来的浏览器渲染机制,并提出相应的优化原则

浏览器如何渲染网页

  1. 浏览器将从服务器中获取的HTML文档逐步解析,构建DOM树

  2. 在构建DOM树时,如果碰到JS和CSS,会加载执行并阻塞HTML的解析,即HTML解析器会将控制权交给JS或CSS解析器,当这个元素被解析完之后,将控制权重交回给HTML解析器,直到整个DOM树构建完成

  3. css样式被载入和解析后,会构成重叠样式表CSSOM

  4. 在DOM和CSSOM的基础上,渲染树(rendering tree)将会被创建,代表一些列将被渲染的对象。渲染树映射除了不可见元素外的所有DOM结构。每段文本字符串都将被划分在不同的渲染对象中,每个渲染对象都包含了它相应的DOM对象以及计算后的样式。即,渲染树是DOM的直观表示

  5. 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout(浏览器使用的一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素,..tables需要多次pass绘制,pass表示像素处理和定点处理)

  6. 最后布局完成,渲染树将转化为屏幕上的实际内容,即绘制painting

重绘Repaint

当页面中的元素样式不改变元素在文档流的位置时,浏览器只会将新样式赋予元素并进行重绘操作

回流Reflow

当改变影响文档内容或者结构,或者元素位置时,回流操作就被触发,一般有下面几种情况

  • DOM操作,对元素的增删改,顺序变化等
  • 内容变化,包括表单区域中的文本改变
  • CSS属性的更改或者重新计算
  • 增删样式表内容
  • 修改class属性
  • 浏览器窗口变化
  • 伪类样式激活

浏览器如何优化渲染

浏览器本身会尽可能的减少重绘或回流的次数,只更改必要的元素。例如position设置为absolute/fixed的元素只会影响其本身和其子元素,而static的元素变化则会影响其之后的所有的页面元素

另外一项技术则是,在js代码运行时,浏览器会缓存所有的变化,然后只通过一次pass绘制操作来应用这些更改。例如下面这些代码就只触发了一次重绘和回流

var $body = $('body');
$body.css('padding', '1px'); // 触发重绘与回流

body.css(color,red);// body.css(‘margin’, ‘2px’); // 触发重绘与回流
// 最终只有一次重绘和回流被触发

然而,根据我们提到过的,获取某个元素的属性会触发强制回流,比如在上面的代码做一些修改

var $body = $('body');
$body.css('padding','1px');//触发重绘与回流
$body.css('color'); // 触发强制回流
$body.css('margin', '2px'); // 触发重绘与回流
// 再进行一次回流,最终会有两次回流被触发

因此,我们在修改元素时,应该尽量避免或者合并读取元素的属性,来减少回流触发,优化性能

当然我们有的时候不得不触发强制回流,比如使一个元素的margin-left先为100px,在通过动画变回50px

.has-transition {
   -webkit-transition: margin-left 1s ease-out;
      -moz-transition: margin-left 1s ease-out;
        -o-transition: margin-left 1s ease-out;
           transition: margin-left 1s ease-out;
}

// 此处的属性改变没有动画效果
$targetElem.css('margin-left', 100);

// 再加上动画效果的属性名
$targetElem.addClass('has-transition');

// 这次改变有动画效果
$targetElem.css('margin-left', 50);

事实上这段代码并不像我们注释中写的那样进行,由于每条语句会被缓存,所以我们只能看到最终的结果而不能看到动画,所以我们就需要加一条强制回流语句使得动画效果得以顺利进行

// 此处的属性改变没有动画效果
$targetElem.css('margin-left', 100);

//任意的可能触发回流的语句都可以
$targetElem.css('margin-left')

// 再加上动画效果的属性名
$targetElem.addClass('has-transition');

// 这次改变有动画效果
$targetElem.css('margin-left', 50);

优化渲染效率的几条实践原则

  • 合理书写html,减少嵌套和多余标签,样式放在标签中,js文件放在闭合前
  • 优化css选择器,尽量减少选择器的嵌套
    浏览器中css选择器是从右向左进行匹配的(为什么选择器要从右到左进行匹配),减少嵌套层能提高匹配效率,并且css选择器根据其优先级具有不同的运行效率

    1. ID选择器: #id(因为其唯一性强)
    2. 类选择器: .class
    3. 标签选择器: div
    4. 相邻选择器: a + i
    5. 子元素选择器: ul > li
    6. 通用选择器: *
    7. 属性选择器: input[type=”text”]
    8. 伪类选择器: a:hover
  • 在js中,尽量减少DOM操作,缓存所有内容,包括属性和对象(如果他们要被复用的话),尽量在缓存之后再进行操作(减少回流),最后再添加到DOM中

  • 使用jQuery时,建议遵循jQuery最佳实践
  • 建议通过改变class值来修改样式,选择器越有针对性越好(这也有助于分离页面样式和逻辑)
  • 尽量只对position值为absolute/fixed的元素设置动画
  • 页面滚动时禁用:hover样式效果

了解更多

如果想更深入底层进行了解可以查阅:
1. How browsers work
2. 【译】浏览器渲染:repaint,reflow/relayout,restyle

猜你喜欢

转载自blog.csdn.net/weixin_39653200/article/details/79458119
今日推荐