浏览器渲染HTML页面流程

背景

JavaScript是一种动态类型语言,在编译时并不能准确知道变量的类型,只可以在运行时确定。所以最近在研究V8引擎中JavaScript的编译和执行过程。
在V8中,JavaScript相关代码并非一下完成编译的,而是在某些代码需要执行时,才会进行编译,这就提高了响应时间,减少了时间开销。既然考虑到提高效率,感觉有必要再把页面渲染流程回顾一下。

渲染过程

  • 解析HTML源码,构建DOM Tree
    在DOM 树中,每个HTML标签都有对应的节点,DOM树的根节点对应HTML标签。
  • 解析CSS代码,计算样式,构建CSSOM Tree
    解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置 < 用户设置 < 外链样式 < 内联样式 < html中的style。
  • 合并DOM TreeCSSOM Tree ,构建Render tree(渲染树)
    渲染树会忽略掉不需要渲染的元素,比如headdisplay:none的元素等。而且一大段文本中的每一个行在渲染树中都是独立的一个节点。渲染树中的每一个节点都存储有对应的css属性。
  • 浏览器根据Render tree(渲染树)把页面绘制到屏幕上显示。

阻塞渲染

由于我们的页面包含JS脚本和CSS,浏览器在渲染页面时,遇到linkscript,HTML Paser会被阻塞。
默认情况下,CSS 被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕。JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。所以得关注一下几点:

  • 解析到CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建。
  • 解析到script 标记时,DOM 构建将暂停,直至脚本完成执行。
  • CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。

因此,实际开发过程中,约定的规则:

  • CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
  • JavaScript 应尽量少影响 DOM 的构建。

备注
解析器一旦读取到script标签(内联或外联)时,会阻塞DOM构建,它会去请求脚本并执行完成后才继续执行下去。asyncdefer标签除外,asyncdefer属性会让脚本异步执行,不会阻塞浏览器解析。

repaints and reflows

CSSJavaScript往往会修改DOM或者CSSOM,这就会引起浏览器的重排/重绘过程。
重排就是渲染树的一部分必须要更新 并且节点的尺寸发生了变化。这就会触发重排操作。
重绘部分节点需要更新,但是没有改变他的集合形状,比如改变了背景颜色,这就会触发重绘。
记住,重排一定会引起重绘,而重绘不一定会引起重排。

一、 reflow(重排 / 回流):根据Render tree进行节点信息计算(Layout)。
当Render Tree刚构建完时,并不包含元素节点的位置和大小信息。计算这些值的过程称为重排或回流。

常见的引起重排的操作:

  • 元素尺寸或位置发生改变。

  • 元素内容变化(文字数量或图片大小等等)。

  • 元素字体大小变化。

  • 页面首次渲染。

  • 浏览器窗口大小发生改变。

  • 增加或删除DOM节点设置 display: none;(重排并重绘) 或visibility: hidden(只有重排)

  • 激活CSS伪类(例如::hover)。

  • 设置style属性

  • 查询某些属性或调用某些方法

  • repaint(重绘):根据计算好的信息绘制整个页面(Painting)。
    重绘,就是当页面中元素样式的改变并不影响它在文档流中的位置时,例如更改了字体颜色,浏览器会将新样式赋予给元素并重新绘制的过程称。
    常见的引起重排的操作:

  • 设置colorvisibilitytext-decorationbox-shadow

  • 设置background相关的CSS,如backgroundbackground-imagebackground-positionbackground-repeatbackground-size

  • 设置border相关的CSS,如borderboerder-styleboerder-radious

  • 设置outline相关的CSS,如 outlineoutline-coloroutline-styleoutline-width

优化性能

重排一定会引起重绘,而重绘不一定会引起重排,所以要想提升性能,要从减少重排开始入手。

  • 避免单独设置style属性,可以通过一个class属性来统一设置。
  • 要是对于某节点有大量的操作,建议使用documentFragment,clone该节点,进行修改,然后直接替换以前的节点。
  • 要是样式需要通过计算获取,最好把计算结果缓存起来,而不是每次都是从DOM上读取。
  • 绝对定位元素的动画不会影响其他大部分元素。实现元素的动画,它的position属性,最好是设为absoulte或fixed,这样不会影响其他元素的布局,还可以让动画处于更高的图层(即:z-index的值更大)这也是从图层的角度进行优化的。
  • 动画实现的速度的选择。考虑到资源的消耗,可以适当的放慢,比如动画是以1个像素为单位移动,但是reflow就会过于频繁,如果以3个像素为单位移动则会好很多。
  • 少用table布局。因为table中某个元素旦触发了reflow,那么整个table的元素都会触发reflow。必须要使用的可以设置table-layout:auto或者table-layout:fixed这样可以让table一行一行的渲染。
  • 用 document.createElement 创建的 script 默认是异步的。所以,通过动态添加 script 标签引入 JavaScript 文件默认是不会阻塞页面的。如果想同步执行,需要将 async 属性人为设置为 false。

你可能感兴趣的:(浏览器渲染HTML页面流程)