前沿:浏览器的多进程架构
chrome的多进程架构主要包括以下几个进程:
- 1、浏览器(主)进程:主要负责子进程管理、页面显示(地址栏、工具栏、书签栏等UI元素)、用户交互(处理用户的输入,如点击、滚动、键盘输入等)、数据存储等功能。
- 2、渲染进程:核心是解析和运行html、css、js,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中。默认情况下每个tab标签都有一个独立的渲染进程,但是如果从一个页面打开了一个新页面,而新页面和原页面属于同一站点(协议与根域名相同),那么新页面就会和原页面共用一个渲染进程。
- 3、网络进程:用于处理网络请求,负责与服务器的通信。
- 4、插件进程:负责插件的运行。
-
5、GPU进程:负责处理整个应用程序的GPU任务。
进程之间通过IPC机制进行通信。
输入url到页面显示的流程
整个过程需要各个进程之间的配合
-
用户输入:
浏览器进程接收用户输入,检查url与组装协议,构成完成的url(如果是搜索内容,浏览器默认搜索引擎+搜索关键字,组成新的url;如果输入的内容符合url规则,如输入的baidu.com
,会加上协议https,组成完整的urlhttps://baidu.com
),通过IPC(进程通信机制)将完整url转发给网络进程 -
url请求:
网络进程接收到url请求的流程:
2.1. 首先查找是否已缓存资源,有缓存,直接返回资源给浏览器进程,无缓存发起网络请求,进入到下一步
2.2. DNS解析:获取url对应的ip地址
2.3. 利用IP地址和服务器建立TCP连接(三次握手),如果是https请求还需要建立TLS连接
2.4. 连接建立后,向服务器发送HTTP请求,请求包括请求行(GET 或POST),请求头(User-Agent、Accept、Cookie等信息),请求正文等内容。
2.5. 服务端处理请求生成相应数据(包括响应行、响应头、响应体),并发给网络进程。
2.6. 网络进程接收响应数据。如果返回响应行中的状态码是301/302,说明服务器需要浏览器重定向到其他url,这时网络进程会从响应头中读取Location字段的值也就是重定向的地址,然后发起新的http/https请求(回到2.1)
2.7. 状态码200,检查content-type,如果是字节流类型,会将该请求提交到下载管理器,是html,则通知浏览器进程准备渲染进程 -
准备渲染进程
浏览器进程检查是否需要重开渲染进程,如果有符合复用要求的(旧页面中打开的且属于同一站点)复用原来的进程,如果不同,则开启新的单独的渲染进程 -
渲染
4.1. 渲染进程准备好后,浏览器进程向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”
4.2. 渲染进程接收完数据后,向浏览器发送“确认提交” ,浏览器进程接收到确认消息后更新浏览器界面状态:安全、地址栏url、前进后退的历史状态等。
4.3. 渲染进程对文档进行页面解析和资源加载:- 构建DOM树:渲染进程将html转换为dom树结构
- 样式计算生成styleSheets
- 根据dom树和styleSheets生成布局树(不包括隐藏的DOM元素,如display:none的元素、和head元素)
- 对布局树进行分层,并生成分层树(layerTree)
- 渲染引擎将每个图层的绘制拆分成很多小的绘制指令,然后将指令按照顺序组成一个待绘制列表,并提交到渲染进程中的合成线程
- 合成线程将图层划分为图块(tile),然后将图块转换为位图(栅格化),通常栅格化过程都会使用 GPU 来加速生成。所有的图块都被栅格化后,合成线程会生成一个绘制图块的命令“ DrawQuad”,将该命令提交给浏览器进程
-
浏览器进程接收到合成线程发过来的命令,根据命令将页面内容绘制到内存中,再将内存显示在屏幕上
重排(reflow)和重绘(repaint)
-
重排(Reflow):会触发重新布局
-
重绘(Repaint):没有引起几何位置的变换,没有执行布局阶段,直接进入了绘制阶段,然后执行之后的一系列子阶段。重绘并不会造成额外的计算或布局,所以性能影响较小。
减少重排重绘,相当于减少了渲染进程的主线程和非主线程的很多计算和操作,能够加快web的展示。
- 1、使用 class 操作样式,而不是频繁操作 style(触发repaint/reflow的操作尽量放在一起): 如通过使用 class 来集中修改样式,而不是通过 style 一个一个的修改
- 2、 批量dom 操作:如通过虚拟dom计算出操作总得差异,一起提交给浏览器。
- 3、 避免使用 table 布局: 由于浏览器使用流式布局,对 Render Tree 的计算通常只需要遍历一次就可以完成,但 Table 及内部元素除外,通常需要多次计算且需花费3倍同等元素时间。其次,很小的改动可能会导致整个 table 都重新布局
- 4、 Debounce window resize 事件: 防抖可以防止频繁触发重排重绘,例如 window resize 可以设置为 1s 内只可以触发一次
- 5、 dom读写操作分离:
浏览器的渲染队列机制:当我们修改了元素的几何属性,导致浏览器触发重排或重绘时。它会把该操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行这些操作
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
上述操作会上次回触发一次重排+重绘
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
上述操作会上次回触发四次重排+重绘
当我们同时进行DOM属性的读写操作的时候,浏览器在读取属性值之前,需要将之前队列中的所有写操作执行一遍。比如,我们连续写入两次样式,浏览器会合并这两次写操作;但是如果我们在两次写操作中加入一次读操作,那么浏览器就不能合并这两次写操作了,从而产生两次重绘和重排,导致性能的消耗。
因此,将读写操作分离,也就是尽量合并读操作和写操作,避免互相混杂,可以减少浏览器的渲染次数,从而提高性能