1. 浏览器渲染的过程

最近一直想看看浏览器渲染以及前端性能优化的内容,可是在网上找到的都是文字描述,所以想通过网上的知识去对应到chrome performance看到的过程。

先看一个非常简单的页面代码(或者访问DOM-empty):


<!Doctye html>
<html>
<head>
</head>
<body>
  <div> 
    Test dom load.
  </div>
</body>
</html>

然后打开chrome performence查看页面的渲染过程:

查看GPU使用的进程_加载

Send Request                发送网络请求时触发
Receive Response          响应头报文到达时触发
Receive Data                 请求的响应数据到达事件,如果响应数据很大(拆包),可能会多次触发该事件
Finish Loading               网络请求完毕事件
Parse HTML                  浏览器执行HTML解析
Update Layer Tree        (目前还没有找到具体的说明,后面会继续去查找,如果有了解的希望可以告知)。
Paint                              确定渲染树上的节点的大小和位置后,便可以对节点进行涂鸦(paint)
Composite Layers          合成层;当渲染树上的节点涂鸦完毕后,便生成位图(bitmap),浏览器把此位图从CPU传输到GPU


2. Parse Html(without css and js)

今天主要是为了看一下Parse Html的过程,现在讲Parse Html的过程放大来看:

查看GPU使用的进程_加载_02

然后我们按照时间线从左到右来看一下里面的事情(先说一下,这个图怎么看,下面的内容是上面内容的调用,这里也就是上面带图中带有文字说明的内容都是在ParseHtml的周期内调用的内容):

readystatechange(第一个)

说这个的时候就需要说一个事件DOM readystatechange, readyState 属性描述了文档的加载状态,在整个加载过程中 document.readyState会不断变化,每次变化都会触发readystatechange事件。(可以访问查看例子

readyState 有以下状态:
  loading 加载document 仍在加载。
  interactive 互动文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
  complete 完成DOM文档和所有子资源已完成加载。状态表示 load 事件即将被触发。

那么这里的事件执行的是哪一步呢?

这里执行的是interactive。因为这个事件后面紧跟着的是 DOMContentLoaded事件,而且如果你亲自访问这个页面去看一下,在parseHtml前面还有一次readystatechange,那里应该是loading 。

DOMContentLoaded (构建 DOM 树成功)

DOM树渲染完成时触发DOMContentLoaded事件,此时可能外部资源还在加载。这里表示DOM树加载完成。

Recalculate Style(构建 CSSOM 树)

从文字的字面意义理解也就是重新计算样式。为什么是 Re-caculate Style 呢?这是因为浏览器本身有 User Agent StyleSheet,所以最终的样式是我们的样式代码样式与用户代理默认样式覆盖/重新计算得到的。这里也是在构建CSSOM树。

readystatechange(第二个)

从第一个事件的地方可有了解到这里执行的是complete,表示页面上的DOM树和CSSOM树已经形成并且合并成Render树。此时页面上所有的资源都已经加载完成。其实从后面的load事件也可以看出来。

load事件

所有的资源全部加载完成会触发window 的 load事件。

pageshow事件

当一条会话历史记录被执行的时候将会触发页面显示(pageshow)事件。(这包括了后退/前进按钮操作,同时也会在load事件触发后初始化页面时触发)。

以下示例将会在控制台打印由前进/后退按钮以及load事件触发后引起的pageshow事件:

window.addEventListener('pageshow', function(event) {
    console.log('pageshow:');
    console.log(event);
});

Layout

将渲染树上的节点,根据它的高度,宽度,位置,为节点生成盒子(layout)。为元素添加盒子模型。上图中有两个layout,二者之间的不同是Nodes That Need Layout  1/5 of 5
这里的5代表应该是页面上的5个node(文本内容是文本节点),但是对于这个1,还是没有一个明确的说明,但是影响不大,毕竟还是属于layout。

其实看到这里:我们想一下ParseHtml做了哪些内容:

构建DOM树 -> 构建CSSOM树 -> 构建Render树 -> 布局layout

Note: 这里执行的情况是这样的,但是当我们加上内部css和内部js的时候这个步骤就有了不一样的变化。


3. Parse Html(with css and js)

还是先把代码贴出来(可以访问DOM (with css and js)):

<!Doctye html>
<html>
<head>
  <style type="text/css">
    .div {
      color: blue
    }
  </style>
</head>
<body>
  <div class='div'> 
    Test dom load.
  </div>
  <script type="text/javascript">
    var a = 1 + 1;
  </script>
</body>
</html>


然后以同样的方式将chorme performence的过程贴出来:

查看GPU使用的进程_浏览器渲染过程_03

这里就只说和上面图中不一样的地方:

首先是多个两个黄颜色的js相关的内容,一个叫做Evaluate Script(加载js),另一个是Compile Script(js预编译处理,可以查看文章,这里也已经对js文件执行了)。

第二个不同的地方是从第二个readystatechange事件起一直到layout都已经不在ParseHtml内部完成了。这里我简单的去做了一个测试,只有css或者js存在的情况下,还是和现在一样的结果,这里我假设是因为css或者js阻塞了整个页面的渲染过程,因为js和css都有可能对标签进行样式的设置,从而影响了layout的执行。当然这只是我的一个问题,后面我会带着这个问题继续学习!

那么现在ParseHtml就执行了:构建DOM树 -> 构建CSSOM树 -> 构建Render树。

写在最后:至于说DOM/CSSOM/Render树是如何的构建,在网上有很多的文章再说,更有How browsers work,可以进行学习。至于paint, Composite Layers其实和前端页面的渲染有着很大的关系,后面会有文章去学习。