1.DNS域名解析
DNS域名解析协议(Domain Name System)用来获取域名和IP地址映射关系。(DNS大部分情况使用UDP进行传输)
完整域名解析过程(正向解析 域名--ip)
- 首先搜索浏览器的 DNS 缓存,缓存中维护一张域名与 IP 地址的对应表
- 若没有命中,则继续搜索操作系统的 DNS 缓存(host文件 C:\Windows\System32\drivers\etc\hosts)
- 若仍然没有命中,则操作系统将域名发送至本地域名服务器,本地域名服务器查询自己的 DNS 缓存,查找成功则返回结果(注意:主机和本地域名服务器之间的查询方式是递归查询)
- 若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行查询,通过以下方式进行迭代查询(以防根域名服务器压力过大)
- 首先向根域名服务器发起请求,根域名服务器返回顶级域名服务器的IP
- 本地域名服务器拿到这个顶级域名服务器的地址后,就向其发起请求,获取权限域名服务器的地址
- 本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址
- 本地域名服务器将得到的IP返回给操作系统并缓存
- 操作系统将IP返回给浏览器,并缓存
- 浏览器获得IP并缓存
2.建立TCP连接
三次握手
- 客户端给服务端发送syn包
- 服务端回复客户端syn包和ack包
- 客户端给服务端发送ack包
3.发送HTTP请求
基于HTTP协议发送HTTP请求
4.接收HTTP响应
基于HTTP协议接收HTTP响应
5.浏览器解析html并布局渲染
解析html的原因
从网络传给渲染引擎的 HTML 文件字节流是无法直接被渲染引擎理解的,所以要将其解析为渲染引擎能够理解的内部结构,这个结构就是 dom
,所以换句话说,dom
是一种描述html
的内部数据结构。
整个html
的解析过程是顺序解析,并且渐进式的。
顺序指的是从第一行开始,一行一行依次解析;渐进式则指得是浏览器会将解析出来的部分立即展示出来,如果我们做下面这个实验会发现,在断点处第一个div
已经在浏览器渲染出来了:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
first div
</div>
<script>
debugger
</script>
<div>
second div
</div>
</body>
</html>
如何解析和渲染
a.构建DOM
- 将HTML解析为很多tokens
- 将tokens解析为node节点
- 将node节点组合成一个DOM树
b.构建CSSOM
- 解析CSS,并构建一个CSSOM树(过程与构建DOM类似)
c.构建Render Tree(渲染树)
- 使用DOM树和CSSOM树结合构造Render树
d.Layout
- 计算出元素相对于viewport的相对位置并进行布局
e.Point
- 将Render Tree转化为像素,显示在屏幕上
上面概括了浏览器解析html
并对其进行渲染的关键步骤,需要注意的是,上面的5个过程并不是依次进行的,而是存在一定交叉
a.构建DOM
字节流(二进制数据)->转码成字符->解析成Tokens->解析成node节点->构建DOM树
分词器会将字节流转换转化为
Token
,分为 Tag Token 和文本 Token。
之后将Token
解析为node
节点并将节点添加到dom
上是同时进行的。下面展示一个示例来演示token
解析和node
添加的过程
<html>
<body>
<div>1</div>
<div>test</div>
</body>
</html>
html解析器维护了一个token栈结构,这个结构类似于记忆栈(算法题会经常用到),用来记忆当前解析的节点的开始标签startTag
,解析完毕后token栈清空
html解析器开始工作,会先创建一个根为document
的空dom
结构,同时将StartTag document
的 Token
压入栈底,然后经过分词器解析出来的第一个StartTag html Token
会被压入到栈中,并创建一个 html
的 node
节点,添加到 document
上,如下图所示:
然后按照同样的流程解析出来
StartTag body
和 StartTag div
接下来解析出来的是第一个 div 的文本 Token,渲染引擎会为该
Token
创建一个文本节点(注意:文本节点不推入token栈),并将该 Token 添加到 DOM 中,它的父节点就是当前 Token 栈顶元素对应的节点,如下图所示:
再接下来,分词器解析出来第一个
EndTag div
,这时候 HTML 解析器会去判断当前栈顶的元素是否是 StartTag div
,如果是则从栈顶弹出 StartTag div
按照同样的规则,一路解析,最终结果如下图
b.构建CSSOM
构建CSSOM
的过程与构建DOM
类似,当HTML文件解析过程中遇到了link
标签时,会请求对应的CSS文件,当获取到CSS文件后开始解析它(如果遇到了内联样式表即style
标签则直接开始解析 ),也是先将字节流转化为tokens,接着转化为节点并构建成CSSOM树
注:
CSSOM
有一点与DOM
不同,构建DOM
会在浏览器获取到HTML文件(有可能是片段)后立即进行,而构建CSSOM
必须等整个资源的字节流全部收到后才会进行
c.构建Render Tree
- 从DOM树的根节点开始,遍历每个可见节点
- 有些节点是不可见的(例如,脚本标记、meta标记等),他们不会体现在渲染树上
- 设置了
display: none
属性的节点也不会体现在渲染树上 - 对于每个可见的节点,寻找合适的匹配CSSOM的规则并应用到他们身上。
- 匹配完毕Render Tree构建完毕
d.Layout
布局阶段会从渲染树的根节点开始遍历,由于渲染树的每个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。所以浏览器就可以通过这些样式信息来确定每个节点对象在页面上的确切大小和位置,布局阶段的输出就是我们常说的盒子模型,它会精确地捕获每个元素在屏幕内的确切位置与大小。
e.Point
遍历render tree,将render tree 分层形成不同的layer, 栅格化这些layer ,即将render tree上的信息转化为窗口上的每一个像素点。最后通过合并不同的layor来生成新的画面。