[TOC]

参考:

现代浏览器内部机制-渲染进程的生命历程
渲染性能-像素管道—google developer
谷歌出品的:浏览器渲染优化系列课程

css 触发 layout paint composite 参考网站

1 进程之间协同

image.png

浏览器进程主要负责用户交互、子进程管理和文件储存等功能。

网络进程是面向渲染进程和浏览器进程等提供网络下载功能。

渲染进程的主要职责是把从网络下载的 HTML、JavaScript、CSS、图片等资源解析为可以显示和交互的页面。因为渲染进程所有的内容都是通过网络获取的,会存在一些恶意代码利用浏览器漏洞对系统进行攻击,所以运行在渲染进程里面的代码是不被信任的。这也是为什么 Chrome 会让渲染进程运行在安全沙箱里,就是为了保证系统的安全。

2 http请求过程

image.png

3 具体流程

  1. 用户输入URL,浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容+默认搜索引擎合成新的URL;如果用户输入的内容符合URL规则,浏览器就会根据URL协议,在这段内容上加上协议合成合法的URL
    2. 用户输入完内容,按下回车键,浏览器导航栏显示loading状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得
    3. 浏览器进程浏览器构建请求行信息,会通过进程间通信(IPC)将URL请求发送给网络进程
    GET /index.html HTTP1.1
    4. 网络进程获取到URL,先去本地缓存中查找是否有缓存文件,如果有,拦截请求,直接200返回;否则,进入网络请求过程
    5. 网络进程请求DNS返回域名对应的IP和端口号,如果之前DNS数据缓存服务缓存过当前域名信息,就会直接返回缓存信息;否则,发起请求获取根据域名解析出来的IP和端口号,如果没有端口号,http默认80,https默认443。如果是https请求,还需要建立TLS连接。
    6. Chrome 有个机制,同一个域名同时最多只能建立 6 个TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于6个,会直接建立TCP连接。
    7. TCP三次握手建立连接,http请求加上TCP头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输
    8. 网络层在数据包上加上IP头部——包括源IP地址和目的IP地址,继续向下传输到底层
    9. 底层通过物理网络传输给目的服务器主机
    10. 目的服务器主机网络层接收到数据包,解析出IP头部,识别出数据部分,将解开的数据包向上传输到传输层
    11. 目的服务器主机传输层获取到数据包,解析出TCP头部,识别端口,将解开的数据包向上传输到应用层
    12. 应用层HTTP解析请求头和请求体,如果需要重定向,HTTP直接返回HTTP响应数据的状态code301或者302,同时在请求头的Location字段中附上重定向地址,浏览器会根据code和Location进行重定向操作;如果不是重定向,首先服务器会根据 请求头中的If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回304状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;否则,返回新数据,200的状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段:
    Cache-Control:Max-age=2000
    响应数据又顺着应用层——传输层——网络层——网络层——传输层——应用层的顺序返回到网络进程
    13. 数据传输完成,TCP四次挥手断开连接。如果,浏览器或者服务器在HTTP头部加上如下信息,TCP就一直保持连接。保持TCP连接可以省下下次需要建立连接的时间,提示资源加载速度
    Connection:Keep-Alive
    14. 网络进程将获取到的数据包进行解析,根据响应头中的Content-type来判断响应数据的类型,
    如果是字节流类型,就将该请求交给下载管理器,该导航流程结束,不再进行;
    如果是text/html类型,就通知浏览器进程获取到文档准备渲染
    15. 浏览器进程获取到通知,根据当前页面B是否是从页面A打开的并且和页面A是否是同一个站点(根域名和协议一样就被认为是同一个站点),如果满足上述条件,就复用之前网页的进程,否则,新创建一个单独的渲染进程
    16. 浏览器会发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程
    17. 浏览器收到“确认提交”的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新web页面,此时的web页面是空白页
    18. 渲染进程对文档进行页面解析和子资源加载,HTML 通过HTM 解析器转成DOM Tree(二叉树类似结构的东西),CSS按照CSS 规则和CSS解释器转成CSSOM TREE,两个tree结合,形成render tree(不包含HTML的具体元素和元素要画的具体位置),通过Layout可以计算出每个元素具体的宽高颜色位置,结合起来,开始绘制,最后显示在屏幕中新页面显示出来

    4 渲染过程

    4.1 构建DOM树

    这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
    DOM 是保存在内存中树状结构,可以通过 JavaScript 来查询或修改其内容。
    image.png
    在devtool工具中输入document,即可查看DOM树
    image.png

    4.2 样式计算(Recalculate Style)

    样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式

    4.2.1 把 CSS 转换为浏览器能够理解的结构

    image.png
    浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。
    image.png

    4.2.2 转换样式表中的属性值,使其标准化

    image.png
    2em 被解析成了 32px,red 被解析成了 rgb(255,0,0),bold 被解析成了 700

    4.2.3 计算出 DOM 树中每个节点的具体样式

    接下来就需要计算 DOM 树中每个节点的样式属性了,这就涉及到 CSS 的继承规则和层叠规则了。
    CSS 继承就是每个 DOM 节点都包含有父节点的样式(不是所有的属性都会继承)
    层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法
    总之,样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。
    image.png

    4.3 布局阶段

    接下来就需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
    Chrome 在布局阶段需要完成两个任务:创建布局树和布局计算。

    4.3.1 创建布局树

    DOM 树还含有很多不可见的元素,比如 head 标签,还有使用了 display:none 属性的元素。
    所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树。
    image.png
    遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中

    4.3.2 布局计算

    有了一棵完整的布局树。那么接下来,就要计算布局树节点的坐标位置了

    4.4 分层

    页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
    浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面
    image.png
    节点分层的情况:
    第一点,拥有层叠上下文属性的元素会被提升为单独的一层。
    第二点,需要剪裁(clip)的地方也会被创建为图层。

    4.5 图层绘制

    渲染引擎实现图层的绘制,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表
    image.png

    4.6 栅格化(raster)操作

    绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。
    image.png

    4.7 合成和显示

    一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
    然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
    image.png

    4.8 总结

    image.png

    5 performance查看渲染过程

    image.png

    5.1 Parse HTML—构建DOM

    可以看到解析过程中,遇到js会执行,阻塞dom解析, 更多见本文第7节
    image.png

    5.2 Recalculate Style—样式计算

    image.png

    5.3 Layout—布局

    image.png

    5.4 Update Layer Treee—分层

    image.png

    5.5 Paint—绘制

    image.png

    5.6 Composite Layers—合成图层

    image.png

    6 交互过程—像素管道

    copy from 标题链接
    image.png
  • JavaScript。一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如: CSS Animations、Transitions 和 Web Animation API。
  • 样式计算。此过程是根据匹配选择器(例如 .headline.nav > .nav__item)计算出哪些元素应用哪些 CSS 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。
  • 布局。在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 <body> 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。
  • 绘制。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
  • 合成。由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。

管道的每个部分都有机会产生卡顿,因此务必准确了解您的代码触发管道的哪些部分。
有时您可能听到与绘制一起使用的术语“栅格化”。这是因为绘制实际上分为两个任务: 1) 创建绘图调用的列表,以及 2) 填充像素。
后者称为“栅格化”,因此每当您在 DevTools 中看到绘制记录时,就应当将其视为包括栅格化。 (在某些架构下,绘图调用的列表创建以及栅格化是在不同的线程中完成,但是这不是开发者所能控制的。)

不一定每帧都总是会经过管道每个部分的处理。实际上,不管是使用 JavaScript、CSS 还是网络动画,在实现视觉变化时,管道针对指定帧的运行通常有三种方式:

1. JS / CSS > 样式 > 布局 > 绘制 > 合成

image.png
如果您修改元素的“layout”属性,也就是改变了元素的几何属性(例如宽度、高度、左侧或顶部位置等),那么浏览器将必须检查所有其他元素,然后“自动重排”页面。任何受影响的部分都需要重新绘制,而且最终绘制的元素需进行合成。

2. JS / CSS > 样式 > 绘制 > 合成

image.png
如果您修改“paint only”属性(例如背景图片、文字颜色或阴影等),即不会影响页面布局的属性,则浏览器会跳过布局,但仍将执行绘制。

3. JS / CSS > 样式 > 合成

image.png
如果您更改一个既不要布局也不要绘制的属性,则浏览器将跳到只执行合成。
这个最后的版本开销最小,最适合于应用生命周期中的高压力点,例如动画或滚动。

7 浏览器js执行

7.1内联js

我先来解释下页面在含有JavaScript的情况下DOM解析流程,然后再来解释你这个问题。

当从服务器接收HTML页面的第一批数据时,DOM解析器就开始工作了,在解析过程中,如果遇到了JS脚本,如下所示:


极客时间



那么DOM解析器会先执行JavaScript脚本,执行完成之后,再继续往下解析。

7.2 外部js

那么第二种情况复杂点了,我们内联的脚本替换成js外部文件,如下所示:


极客时间



这种情况下,当解析到JavaScript的时候,会先暂停DOM解析,并下载foo.js文件,下载完成之后执行该段JS文件,然后再继续往下解析DOM。这就是JavaScript文件为什么会阻塞DOM渲染。
image.png

7.3 js访问样式—css也会阻塞

我们再看第三种情况,还是看下面代码:





极客时间





当我在JavaScript中访问了某个元素的样式,那么这时候就需要等待这个样式被下载完成才能继续往下执行,所以在这种情况下,CSS也会阻塞DOM的解析。

所以这时候如果头部包含了js文件,那么同样也会暂停DOM解析,等带该JavaScript文件下载后,便开始编译执行该文件,执行结束之后,才开始继续DOM解析。