最近听了winter讲浏览器工作远离的课,感触很深,随即整理了一下,以便加深印象。
工作流程
浏览器对于我们感性上来说,就是我们输入一个链接,它帮助我们展现了一个页面。但是其实在底层,它做了很多工作,大致分为如下几个步骤:
- 通过协议从服务端获取html
- 将html解析为dom树
- 计算dom树的css属性
- 渲染css,在内存绘制位图(类比dom树,会形成一个一样的css属性树)
- 对位图进行合成
- 绘制到页面
上边完成第一步后,后续步骤都是流式进行的,不是一定要等到前一个步骤完成后一个才能进行,而是前一个步骤产生数据,直接交给下一个步骤接着工作。
网络通信
http协议
浏览器首先要做的就是从服务器端拿数据,从服务端拿数据使用的是http协议,http协议是基于tcp协议出现的,tcp协议是一个双向通道,http在tcp协议的基础上规定了Request-Response的模式,这个模式决定了通信必定由浏览器首先发起。
status code和status text
1**的状态码一般很难看到,浏览器的http库一般直接处理掉了,上层应用一般很难遇到。
304一般是因为客户单通过request告诉服务端,本地有缓存,但是服务器检查etag或者时间发现本地没有更新就会返回304给客户端。
http Request body
请求体一般用来发送表单给服务器,请求体其实比较自由,只要服务端认可即可。
https
https使用TLS加密传输内容,Https相比较http有两个作用:
- 确定请求目标的服务端地址
- 保证传输数据不会被窃听和修改
http2
http2有两大改进点:
-
支持服务端推送
能够在客户端发送第一个请求到服务端时,提交把一部分内容推送给客户端,放入缓存中,可以避免客户端请求顺序带来的并行度不高,从而导致的性能问题。
-
tcp连接复用
使用同一个tcp连接来传输多个http请求,减少tcp连接建立三次握手的开销。
解析html
当从服务端拿到html文档后,需要将其解析成dom树,这需要用到状态机的知识,解析器会根据拿到字符逐个分析,每次拿到新内容的解析都会根据目前已经拿到的内容作为分析基础进行分析,这里不过分深入说明其中知识,只大致说明其中发生了哪些事情,下边是一个简单的例子:
当有
text
这样一段html代码,到解析器拿到第一个字符'<',就会判断出这内容不会是字符内容,当拿到第二个字符是'p'就会判断出要解析的内容不会是注释或者CDATA内容,以此类推慢慢解析出整个dom树。匹配css
css选择器这个称呼会让很多人误解,以为css是构建好dom树,然后一个个查找dom元素然后将样式添加上去,其实并不是这样的,前边已经说过,从服务端拿到html内容后,后续操作都是流式操作的,所以绝不会是查找添加这种方式添加样式的。
其实在构建dom树的过程中会同步的计算dom节点的css属性,当节点挂载在dom树上后,就会根据规则匹配给它添加css样式,然后节点根据优先级做覆盖和调整。所以所谓的选择器称为"匹配器"更合适。
css选择器
- 空格: 后代,选中它的子节点和所有子节点的后代节点。
- >:子代,选中它的子节点。
- +:直接后继选择器,选中它的下一个相邻的节点
- ~:后继,选中它之后所有的相邻节点。
- ||:列,选中表格的一列。
因为css样式的添加和dom树构建顺序是同步的,所以选择器的匹配顺序必定和dom树的顺序一致,这是css设计原则,保证选择器在dom树构建到当前节点已经可以准确添加它的样式,所以以后也不会出现类似父选择器这种选择器。
匹配
过于详细的说明这里就不过多阐述了,大致是浏览器将css内容按照匹配规则抽象成一颗css树,dom树中节点根据css树和自身节点位置是否符合条件来添加css。
排版
先说说几个概念:
排版:浏览器确定文字、图片、表格等位置的过程,称为排版。
盒模型:浏览器中的元素占据长方形区域:还允许边框、边距、留白,这个就是所谓的盒模型。
正常流排版:浏览器最基本的排版方案就是正常流排版,包含了顺序排布(依次按照html出现顺序添置元素)和换行(当一行内容不够放置时候换行)等规则。
正常流排版
块级元素
直接占据一行,比较容易理解
行内元素
根据width、padding和margin确定主轴占据距离,再根据vertical-align确定垂直占据位置,同时也会影响行高。
绝对定位元素
绝对定位元素完全脱离文档流,逐层找到父级非static定位元素,来确定位置,并且它还不占据空间
浮动元素
浮动元素比较特殊,浏览器会先处理其他正常流元素排版,在移动浮动元素到主轴最前或者最后,移动后浮动元素所在的行会重新确定位置,浮动元素相比绝对定位元素的区别是浮动元素会占据空间。
展示页面
这是最后的步骤了,确定好元素的位置后就是展示最终的页面给客户端。
渲染
渲染在计算机图形学领域指把模型变成位图的过程,对应到浏览器中,渲染局势指将元素(html元素和伪元素)对应的盒变成位图,渲染的过程非常复杂,大体可以分为两类:图形和文字。
盒的背景、边框、SVG 元素、阴影等特性,都是需要绘制的图形类,注意:这里渲染的过程不会把子元素绘制到渲染的位图上,这样当父元素变化时候,可以保证渲染的结果能够最大程度缓存,减少重新渲染。
合成
合成:上边提到的渲染不会渲染子元素,这里的合成就是为一些元素创建一个"合成的位图",后续绘制整个位图的时候可以减少操作。这里的合成策略很复杂,因为确定哪些元素需要合成哪些不需要合成是个很复杂的事情,因为合成位图过大的话,后续经常变更,需要重新变更起不到提高性能的意义,比较好的合成策略,是能够准确判断出哪些经常变更,将它抛出合成范围里。现在的前端框架很多都提供这个方法,让你设置合成策略,例如angular的will Change生命周期函数。
绘制
绘制就是将位图绘到屏幕上变成肉眼可见的图像。一般,浏览器只要把最终位图交给操作系统或者显示驱动即可,取决于浏览器运行环境。
然而很多时候需要进行优化,例如当鼠标在页面移动,随着鼠标箭头变多,位图需要不断刷新,如果只是简单的重新绘制位图,性能就会太低,一般都会使用一些算法去优化处理,上边这个例子一般使用脏矩形算法,将屏幕均匀分成若干个矩形区域,只要重新绘制影响的那几个区域即可。