漫谈浏览器渲染

file

本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。

多进程/多线程模型

主要进程类型

  1. 浏览器进程:浏览器主进程,仅有一个,用于进程、资源调度和控制。
  2. 渲染进程:基本每个浏览器标签页都是一个渲染进程,在内存紧张的时候会合并成一个进程
  3. GPU进程:用户绘制3D图形和动画绘制
  4. 第三方插件进程:浏览器有很多插件,都运行在第三方插件进程里,防止插件进程影响到主进程等其他进程

打开 Chrome 的任务管理器的窗口,如下图:
image
image

渲染进程的主要线程类型

  1. UI渲染线程(renderer thread):渲染进程的主线程
    1. 解析HTML,CSS,构建DOM树和CSSOM树,布局和绘制等
    2. 当HTML解析script标签时,就会解析script里的Javascript脚本程序(Chromium使用的是V8,Safari用的是JavaScriptCore),阻塞html的解析
  2. I/O线程
    1. 负责转发渲染进程与浏览器主进程之间的通信消息
    2. 负责网络资源请求加载,比如UI渲染线程解析到link标签、img标签、script标签加载外部资源的时候就会通知到I/O线程转发加载资源消息到浏览器主进程的网络线程
  3. 其他线程
    1. Worker线程(web worker、service worker)
    2. 光栅化线程(raster thread):当一个个layer tree创建完并且绘制顺序确定了之后,就开始光栅化成一个个位图
    3. 复合线程(compositor thread):合成一张张render layer位图合成一张位图

浏览器中可看到各线程情况:
image

渲染过程

以下面的例子结合Performance面板看看渲染流程

// parsing.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    .test1 {
      color: pink;
    }
  </style>
  <link href="https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.css" rel="stylesheet" />
  <link rel="stylesheet" href="./parsing.css" />
  <style>
    html, body {
      margin: 0;
      padding: 0;
    }
  </style>
  <script>
    console.log("test1");
  </script>
</head>

<body>
  <div class="test1">123</div>
  <style>
    .test2 {
      color: purple;
    }
  </style>
  <div class="test2">
    <img src="http://i1.cmail19.com/ei/j/2A/BC7/816/202259/csimport/actionrocketdarkmodelogooutlinev2_0.png"
      alt="cdn" />
  </div>
  <div class="test3">456</div>
  <style>
    .test3 {
      color: green;
    }
  </style>
  <script>
    console.log("test2");
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fastdom/1.0.10/fastdom.js"></script>
  <div class="test4">789</div>
  <style>
    .test4 {
      color: blue;
    }
  </style>
  <script async="true">
    console.log("test3");
  </script>
  <div class="test5">10</div>
  <style>
    .test5 {
      color: orange;
    }
  </style>
</body>
</html>
// parsing.css
.test1 {
  background-color: green;
}

首次渲染过程分析

Parsing

UI渲染线程拿到HTML文档的时候开始解析html 标签成DOM树。UI渲染线程在执行Parsing的过程也会启动一个Preload Scanner进行扫描正在形成的DOM树上有没有img、link等加载外部资源的标签或者属性,如果发现就会通知I/O线程转发加载外部资源的请求给浏览器主进程的网络线程进行资源加载。

Parsing html文档里的0-21行文本

如果没有script标签等阻塞parsing过程的,那么这个Parsing html的过程就会覆盖整个html
image

Parsing过程中查找外部资源

Preload Scanner会发现整个html文档里的外部资源并且发出预请求,不只是发现0~21行html文档里的网络资源
下图是Parsing过程中发现有link标签引入了parsing.css,于是开始请求这个外部css
image

JavaScript

UI渲染线程在执行Parsing的过程一旦解析遇到script等包含JavaScript代码的标签或者属性,就会立马停止Parsing,去加载JavaScript代码,调用V8去解析、执行JavaScript代码。因为JavaScript代码会有DOM API操作会改变DOM树,比如appendChild、removeChild等涉及到html元素布局的修改,或者是dom.style = "color: red"之类的操作html对应的css样式。

Parsing与JavaScript过程遇到script标签不一定非要加载完script标签再执行script里的JavaScript代码的,script标签的defer和async属性就是改变Parsing过程中遇到script标签的行为。

正常执行:没有async和defer属性

JavaScript下载和执行会阻塞parsing的过程

上面的html代码就是这个过程
image

  1. Parse html 0~21行
    image.png
  2. Parse html 22~42行

image.png

  1. Parse html 43~-1行
    image
有async属性

JavaScript异步下载不会阻塞parsing的过程,但是下载完之后会立即执行代码阻塞Parsing的过程

修改一下示例html,将加载fastdom的script标签加上async属性:

重新用performance分析可以发现Parsing的过程与下载script的过程也是并行的,但是Parsing过程仍然受JavaScript执行时机影响

image

有defer属性

JavaScript异步下载不会阻塞parsing的过程,下载完之后不会立马执行,代码执行放在DOMContentLoaded之后执行

修改一下示例html,将加载fastdom的script标签加上defer属性:

重新用performance分析可以发现Parsing的过程与下载script的过程也是并行的,Parsing过程可以比下载script的结束和执行代码前

image

Style

HTML DOM树解析完之后,会进行样式计算:在这个过程中会根据匹配选择器(例如 .header 或 .footer > .confirm-button)计算出哪些元素应用哪些 CSS 规则,建立一个CSSOM树。如果修改了一个元素的html结构、css属性值,都会触发样式的重新计算。如果css选择器写太复杂,这个过程时间就会耗时长一些。

构建基本的CSSOM

image

重新计算CSSOM树

image

Layout

当DOM树、CSSOM树都已经建立好了,但是却只是这些对象的自身的描述,还不知道这些对象如果最终要渲染在页面上的真正位置信息。于是就有这个Layout的过程根据DOM树、CSSOM树的节点位置信息计算、整合、建立出一棵Layout树。

建立layout树

image

更新layer树

保证绘制顺序
image

Paint

有了Layout树,根据Layout树开始绘制位图,但是由于CSS有z-index、float等改变文档流等属性会造成屏幕的垂直方向上的元素重叠,那就需要先按照垂直方向上的顺序(Stacking Order)进行绘制元素,如果垂直方向上同一层的元素就按照html的顺序绘制元素。绘制的过程也叫光栅化(Raster),光栅化之后的结果是一张render layer位图。

查看Paint绘制时机

image

查看Paint绘制过程

勾选左上角的paint instrumentation选项

image.png

Composite

一般网页应用当然不会只有一张render layer位图,如果HTML元素使用scaleZ、will-change等属性会专门新建一个render layer去给这元素进行渲染,所以提升成一个独立的render layer之后自然Style、Layout、Paint等过程的消耗自然会小很多。但是render layer增加了之后也需要合并这些render layer的位图成一张位图最终展示在屏幕上,因此需要进行Composite(复合)render layer。
image

单独提升渲染层

给某一个元素加上transform: scaleZ(0)的css属性

image.png

image

渲染更新过程分析

这个网站可以看到哪些css属性会触发哪一个过程:https://csstriggers.com/

Relayout

image

用户触发事件,开始执行JavaScript操作DOM,改变元素的几何属性(比如width、margin-left),就会触发Style过程重新计算样式规则,再触发Layout过程生成新的Layout树,再触发Paint过程绘制位图,最后再触发Composite过程将多张位图合成一张位图。

Repaint

image

用户触发事件,开始执行JavaScript改变DOM,改变元素的外观属性(比如color、background),除了不会触发Layout过程,其余过程与Relayout过程一样。

Composite

image

用户触发事件,开始执行JavaScript改变DOM,改变元素会提升渲染层的属性(比如scaleZ、will-change),那么此时就会在独立渲染层执行Layout、Paint,性能消耗忽略不计。而主文档流的渲染层就会跳过Layout、Paint的过程,直接与独立渲染层进行合成。

如何提高渲染性能

减少长时间的JavaScript执行

不仅上面提到的首次渲染过程中的Parsing会被JavaScript执行打断,渲染更新过程中的Style和Layout过程也是会被JavaScript的执行打断。如果长时间执行JavaScript,那么就会阻止Parsing、Style、Layout的过程,于是用户就会在这段时间内发现页面样式、布局没有改变且操作无法响应。

减少样式选择器的数量和复杂度

Style过程取决于css选择器复杂度,如果css选择器过多以及过于复杂,那么就会影响计算CSSOM的速度。

避免大型、复杂的布局

Parsing过程速度、Layout过程速度与HTML标签数量、嵌套层级、复杂布局呈正相关。如果频繁地触发Relayout消耗比较大,并且Relayout基本作用于整个HTML文档的。

适当使用提升元素渲染层

做动画的时候,需要适当地使用will-change提升动画元素的渲染层,脱离主文档流的渲染层,避免频繁改变主文档流的布局,造成Relayout、Repaint的性能浪费。

参考

Rendering Performance

Taobao FED | 淘系前端团队

The stacking context - CSS: Cascading Style Sheets | MDN

欢迎大家留言讨论,祝工作顺利、生活愉快!

我是bigo前端,下期见。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java安全漫谈是一本关于Java安全的书籍,深入探讨了Java应用程序在网络环境中的安全性和相关的安全漏洞。该书内容涵盖了Java安全基础、Java虚拟机的安全机制、Java安全管理、Java安全开发等方面的知识。 首先,Java安全基础部分介绍了Java安全模型的原理和特点,包括Java类库的安全特性、权限管理和访问控制、安全策略配置等。这部分内容可帮助开发人员了解Java应用程序的安全需求,并提供相应的解决方案。 其次,Java虚拟机的安全机制是Java应用程序的基石。该书介绍了Java虚拟机的安全沙箱和类加载机制,并讨论了如何利用这些安全机制避免恶意代码的执行和隐患的防范。 此外,Java安全管理部分从用户角度出发,介绍了Java应用程序的安全管理工具和技术,如Java安全策略文件、权限管理和安全认证等。开发人员可以通过合理配置和使用这些工具来提高Java应用程序的安全性。 最后,该书还涉及了Java安全开发过程中的一些最佳实践和常见安全漏洞,如输入验证、跨站脚本攻击(XSS)、SQL注入、跨站请求伪造(CSRF)等。通过学习和掌握这些知识,开发人员可以编写出更加安全的Java应用程序。 总而言之,Java安全漫谈是一本全面讨论Java安全的书籍,内容涵盖了Java安全基础、Java虚拟机的安全机制、Java安全管理和Java安全开发等方面的知识。它对于开发人员和安全从业人员来说,都是一本重要的参考书,有助于提高Java应用程序的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值