一、前置知识
1、现代的浏览器进程
现代的浏览器进程包括:浏览器进程、网络进程、渲染进程以及GPU进程和插件进程。为了理解URL到页面渲染发生了什么,你必须理解并记住前三个进程。
- 浏览器进程负责界面显示、和用户交互、子进程管理、存储功能等。
- 网络进程负责页面的网络资源的加载。
- 渲染进程负责将HTML、CSS、JS转化为用户可以与之交互的网页,排版引擎和V8引擎也在其中运行。默认情况下,每一个Tab标签享有一个渲染进程。渲染进程运行在沙箱下。渲染进程包括渲染主线程、IO线程、合成线程、栅格化线程(池).
- GPU进程负责实现3D CSS效果,绘制UI界面。
- 插件进程负责插件的运行,把插件运行在单独的进程确保插件进程崩溃不会对浏览器和页面造成影响。
2、事件循环
事件被分为内部消息类型事件和页面事件.
内部消息事件包括输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、JavaScript定时任务等等。页面事件包括JavaScript执行、解析DOM、样式计算、布局计算、CSS动画。
上述所有事件的处理都发生在渲染进程.
事件循环都是发生在渲染进程的。渲染进程维持了两个线程:渲染主线程和IO线程。渲染进程还维持了一个消息队列(先进先出)。渲染主线程和IO线程通过消息队列来通信。
- 浏览器进程、网络进程把事件通过IPC通信交给渲染进程,渲染进程的IO线程把事件按顺序封装成任务放到消息队列里面。消息队列里面的任务会按先进先出的顺序交给渲染进程的主线程处理,渲染进程处理时有时会依赖其他进程如GPU进程。
二、从输入URL到页面渲染发生了什么?
1、导航流程
-
首先浏览器进程接收到用户输入,按下回车之后,会触发beforeunload事件,在页面当前页面退出之前执行一些清理等操作,若当前页面没有监听 beforeunload 事件或者beforeunload事件同意了继续后续流程,,则会判断用户输入是不是一个url,若是,先使url合法化再交给网络进程,不是则会先根据搜索引擎合成一个url再交给网络进程。
-
浏览器进程会根据进程间通信IPC把url请求发送给网络进程,.首先网络进程会查找本地缓存,若有请求的资源则直接返回给浏览器进程.没找到则网络进程根据url发起真正的请求
- 网络请求第一步是DNS解析,以请求域名的服务器IP地址,如果请求协议是HTTPS,那么还要建立TLS连接
- 第二步是根据IP地址和服务器建立TCP连接.连接建立之后,浏览器会构建请求行,请求头等信息,并把和该域名相关的cookie等数据附加到请求中,然后向服务器发送构建的请求信息.并等待服务器返回信息
- 第三步是根据服务器响应内容执行不同的操作,先看响应的状态码,如果是301则表示临时重定向,则会根据响应头中的location字段重新构建一个网络请求并发送,如果是200,则浏览器处理开始处理服务端发来的响应
- 第四步处理响应,先看响应头中的Content-Type值,若Content-Type是application/octet-stream,会按照下载类型来处理,并把它交给浏览器的下载管理器;如果是text/html,则网络进程会将HTML数据交给浏览器进程
-
如果新打开的页面和当前页面是同一个站点,那么新页面会复用父页面的渲染进程,若不是同一个站点,则浏览器会新创建一个渲染进程
-
浏览器进程接收到HTML数据之后,就向渲染进程发起"提交文档"的消息给渲染进程,渲染进程在接收到提交文档的消息之后,会和网络进程建立数据传输的管道并传输数据,等待数据传输完毕之后,渲染进程会返回"确认提交"的消息给浏览器进程.浏览器在接收到"确认提交"之后,会更新浏览器的界面状态,这时候浏览器出现白屏.
2、渲染流程
- 首先渲染进程的主线程根据HTML内容解析出DOM树,DOM树和HTML几乎是一模一样的,只是我们可以通过JavaScript来查询或修改DOM树,DOM树没有样式。
- 然后是计算样式,样式的来源是link引用的外部CSS文件,style标签里面的CSS,HTML标签里内嵌的CSS。然后浏览器会根据这些计算出styleSheets。然后会对stylesheets进行属性值的标准化,如2em--->30px,red--->rgb(255,0,0),bold--->700。最后计算出DOM树中每个节点的具体样式,这里涉及到继承、层叠等规则,最后把每一个结点的具体样式保存在ComputerStyle的结构内。
- 然后是布局阶段,首先要遍历布局树中所有的结点加到布局树中,即创建布局树。然后是根据计算出布局树中内容计算出每个结点的几何位置,又保存在布局树中。
- 然后是分层阶段,渲染引擎还需要为特定的节点生成专用的图层,用来实现一些复杂的3D变换、页面滚动、或者z轴排序等,此阶段生成一颗对应的图层树。
- 然后是图层绘制,图层绘制不是真的把图层画到页面上,而是生成一个包含绘制顺序和绘制指令的绘制列表
- 然后是生成图块和栅格化操作,当图层列表准备好之后会把绘制列表提交给合成线程。合成线程会把图层划分为图块,并把图块交给栅格化线程池,栅格化线程池会根据图块生成位图,栅格化可能会进行GPU加速,如果使用了GPU加速,那么生成的位图会被保存在GPU内存中。
- 然后是显示内容,一旦所有的图块都被光栅化,合成线程就会生成一个drawquad并提交给浏览器进程,然后浏览器进程将合成线程的位图或GUP的位图内容都绘制到内存中,最后再将内存显示到屏幕上。
三、总结
1、导航流程
-
输入URL后回车,首先会触发当前页面的beforeunload事件,若当前页面没有beforeunload或者同意了beforeunload事件,则继续后续流程
-
然后将url通过IPC通信交给网络进程。网络进程首先会查找本地缓存,若找到则直接返回请求的资源给浏览器进程,没有则进行真正的网络请求
-
首先网络进程会进行DNS,请求IP地址
-
然后根据IP地址进行TCP连接
-
然后构建请求,发送给服务端,并等待返回
-
网络进程先看状态码,若为301等则根据location头部中提供的域名地址重新构建一个请求,若为200则继续
-
网络进程再看响应头的Content-Type,若为application/octet-stream,则把数据交给下载管理器,导航结束;若为text/html,则把数据交给浏览器进程,继续导航
-
浏览器进程先看新的页面和老的页面是否来自同一站点,若为同一站点则新的页面会复用父页面的渲染进程;若不为同一站点则新创建一个渲染进程
-
浏览器进程向新页面的渲染进程发送一个“提交文档”的消息,并建立管道传输数据,渲染进程接收完所有数据之后会发送一个“确认提交”的命令给浏览器进程,浏览器进程就更新浏览器的界面状态,浏览器出现白屏。
2、渲染流程
DOM树、styleSheets、布局、分层、图层绘制、图块栅格化生成位图、合成与显示
- 渲染进程首先将HTML转化为能够读懂的DOM树结构
- 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式
- 创建布局树,并计算元素的布局信息
- 对布局树进行分层,并生成分层树
- 为每个图层生成绘制列表,提交给合成线程
- 合成线程将图层划分为图块,在光栅化线程池中将图块转化为位图
- 合成线程发送DrawQuad命令给浏览器进程
- 浏览器进程接收DrawQuad消息根据位图生成页面,并显示到浏览器上
四、什么是重排、重绘和合成
1、更新了元素的几何属性,会触发重排
重新创建布局树以及之后的一系列浏览器工作
例如:通过JavaScript修改元素的高度,就会触发重排.
2、更新了元素的绘制属性,会触发重绘
重绘会跳过创建布局树和图层树的阶段,进行其他一系列的操作
例如:通过JavaScript修改了颜色,那么触发重绘.
3、更新了几何属性和绘制属性之外的属性会触发合成
合成会直接在合成线程上进行操作.