- 用户输入URL,浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容+默认搜索引擎合成新的URL;如果用户输入的内容符合URL规则,浏览器就会根据URL协议,在这段内容上加上协议合成合法的URL
- 用户输入完内容,按下回车键,浏览器导航栏显示loading状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得
- 浏览器进程浏览器构建请求行信息,会通过进程间通信(IPC协议(进程通信协议))将URL请求发送给网络进程
- 网络进程获取到URL,先去本地缓存中查找是否有缓存文件,如果有,拦截请求,直接返回200(from cache) ;否则,进入网络请求过程
- 网络进程请求DNS返回域名对应的IP和端口号,如果之前DNS数据缓存服务缓存过当前域名信息,就会直接返回缓存信息;否则,发起请求获取根据域名解析出来的IP和端口号,如果没有端口号,http默认80,https默认443。如果是https请求,还需要建立TLS连接。
- Chrome 有个机制,同一个域名同时最多只能建立 6 个TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,(优化这个问题的办法在这里[https://www.sojson.com/blog/98.html])(https://www.sojson.com/blog/98.html)
,直至进行中的请求完成。如果当前请求数量少于6个,会直接建立TCP连接。 - TCP三次握手建立连接,http请求加上TCP头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输
- 网络层在数据包上加上IP头部——包括源IP地址和目的IP地址,继续向下传输到底层
- 底层通过物理网络传输给目的服务器主机
- 目的服务器主机网络层接收到数据包,解析出IP头部,识别出数据部分,将解开的数据包向上传输到传输层
- 目的服务器主机传输层获取到数据包,解析出TCP头部,识别端口,将解开的数据包向上传输到应用层
- 应用层HTTP解析请求头和请求体,如果需要重定向,HTTP直接返回HTTP响应数据的状态code301或者302,同时在请求头的Location字段中附上重定向地址,浏览器会根据Location进行重定向操作;如果不是重定向,首先服务器会根据 请求头中的If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回304状态码,协商缓存,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;否则,返回新数据,200的状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段:Cache-Control:Max-age=?,响应数据返回到网络进程。
- 数据传输完成,TCP四次挥手断开连接。如果,浏览器或者服务器在HTTP头部加上如下信息(Connection:Keep-Alive ),TCP就一直保持连接。保持TCP连接可以省下下次需要建立连接的时间,提示资源加载速度
- 网络进程将获取到的数据包进行解析,根据响应头中的Content-type来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器(属于浏览器进程),该导航流程结束,不再进行;如果是text/html类型或者其他类型,就通知浏览器进程获取到文档准备渲染
-
浏览器进程获取到通知,根据当前页面B是否是从页面A打开,并且和页面A是否是同一个站点(根域名和协议一样就被认为是同一个站点),如果满足上述条件,就复用之前网页的进程,否则,新创建一个单独的渲染进程
- 浏览器会发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程
- 浏览器收到“确认提交”的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新web页面,此时的web页面还是空白页,接下来就到了渲染进程来工作了。
浏览器渲染进程主要流程
-
渲染进程对文档进行页面解析和子资源加载,
1.渲染进程将HTML内容转换为能够读懂的DOM树结构
2.渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式。
3.创建布局树,并计算元素的布局信息
4.对布局树进行分层,并计算元素的布局信息
5.为每个图层生成绘制列表、并将其提交到合成线程
6.合成线程将图层分成图块,并在光栅线程池中将图块转成位图
7.合成线程发送绘制图块命令DrawQuad给浏览器进程
8.浏览器进程根据DrawQuad消息生成页面,并显示到显示器上
HTML解析
HTML Parser的任务是将HTML标记解析成DOM Tree,举个例子
举个例子:一段HTML
<html>
<head>
<title>Web page parsing</title>
</head>
<body>
<div>
<h1>Web page parsing</h1>
<p>This is an example Web page.</p>
</div>
</body>
</html>
经过解析之后的DOM Tree差不多就是
将文本的HTML文档,提炼出关键信息,嵌套层级的树形结构,便于计算拓展。这就是HTML Parser的作用。
CSS解析
CSS Parser将CSS解析成Style Rules,Style Rules也叫CSSOM(CSS Object Model)。
StyleRules也是一个树形结构,根据CSS文件整理出来的类似DOM Tree的树形结构:
与HTML Parser相似,CSS Parser作用就是将很多个CSS文件中的样式合并解析出具有树形结构Style Rules。
JavaScript解析
浏览器解析文档,当遇到<script>标签的时候,会立即解析脚本,停止解析文档(因为JS可能会改动DOM和CSS,所以继续解析会造成浪费)。
如果脚本是外部的,会等待脚本下载完毕,再继续解析文档。现在可以在script标签上增加属性 defer或者async。
脚本解析会将脚本中改变DOM和CSS的地方分别解析出来,追加到DOM Tree和Style Rules上。
呈现树(Render Tree)
Render Tree的构建其实就是DOM Tree和CSSOM Attach的过程。
呈现器是和 DOM 元素相对应的,但并非一一对应。Render Tree实际上就是一个计算好样式,与HTML对应的(包括哪些显示,那些不显示)的Tree。
布局Layout
创建渲染树后,下一步就是布局(Layout),或者叫回流(reflow,relayout),这个过程就是通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸,将其安置在浏览器窗口的正确位置,而有些时候我们会在文档布局完成后对DOM进行修改,这时候可能需要重新进行布局,也可称其为回流,本质上还是一个布局的过程,每一个渲染对象都有一个布局或者回流方法,实现其布局或回流。
对渲染树的布局可以分为全局和局部的,全局即对整个渲染树进行重新布局,如当我们改变了窗口尺寸或方向或者是修改了根元素的尺寸或者字体大小等;而局部布局可以是对渲染树的某部分或某一个渲染对象进行重新布局。
绘制(Painting)
1.在绘制阶段,系统会遍历呈现树,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是有渲染引擎中的合成线程来完成的。
当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程。
然后合成线程会按照视口附近的图块来优先生成位图,实际生成位图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓的栅格化,是指图块转换为位图。运行方式如下图所示:
通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者GPU 栅格化,生成的位图被保存在 GPU 内存中。
GPU 操作是运行在 GPU 进程中,如果栅格化操作使用了 GPU,那么最终生成位图的操作是在 GPU 中完成的,这就涉及到了跨进程操作。如下图所示:
最终一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程,最后通过合成线程的DrawQuad命令,将其页面内容绘制到内存中,最后在将内存显示在屏幕上。
关于介绍网络传输性能优化这项工作之前,我们需要了解浏览器处理用户请求的过程,如下图:
关于如何在渲染过程中进行网站性能优化实战,请参考https://juejin.im/post/5b6fa8c86fb9a0099910ac91
参考链接
https://segmentfault.com/a/1190000010298038#articleHeader8
https://time.geekbang.org/column/article/118826