当我们访问一个网站的时候发生了什么?
当我们在地址栏输入一个网址并回车时, 首先需要进行 DNS 解析. 因为不管是 TCP 还是 UDP, 都是基于 IP 的, 我们需要先拿到 IP 才能进行通信.
挺普通的, 首先查询浏览器 DNS 缓存, 然后查询本地 DNS 缓存, 继续查询 DNS 服务器, 总之最后会得到一个 IP.
然后的话, 我们就可以向这个 IP 发包了. 但实际上, 除了 IP 我们还需要一个 MAC 地址. IP 地址是可以改变的, MAC 地址是不会改变的.
判断是否在同个广播域
首先操作系统会检查这个 IP 是否在广播域中, 也就是计算目标和源(我方)的网络地址
如果计算出来的网络地址相同, 那么在同一个广播域中.
发送 ARP 请求
在同个广播域
如果在同个广播域的话, 那么操作系统会广播一个 ARP 请求, MAC 地址设置成 FF:FF:FF:FF:FF:FF, 查询 ip 是之前 DNS 解析出来的 ip. 这样所有广播域的系统都会收到这个请求, 只有 ip 正确的系统才会响应, 并告诉我方正确的 MAC 地址. 这样我们就拿到了 MAC 地址, 可以进行通信了.
不在同个广播域
如果不在同个广播域的话, 操作系统会查看自己的路由表, 根据路由表决定发到哪个网关. 这个路由表一般通过 DHCP 自动配置, 也可以手动配置.
当操作系统决定发到哪的时候, 如果 ARP 缓存里没有找到网关的 MAC 地址, 也会发送一次 ARP 请求, 不过目标是网关 IP, 只有网关会回答自己.
正式发送请求
在同个广播域的情况下现在已经可以通信了.
不在同个广播域的话, 操作系统会将目标 IP 设置成之前 DNS 解析的 IP, MAC 地址设置成网关 MAC 地址, 然后发送给网关. 网关会进行解包, 发现 IP 地址不是自己后, 会将 TTL-1 再打包, 然后根据路由表来转发数据包. 不断进行这个过程, 如果期间 TTL 为 0 了就会丢弃这个包, 最后发送给真正的目标.
正式交换数据的过程就不再写了.
浏览器拿到 HTML 会做什么
解析 HTML
在浏览器的网络线程收到 HTML 文件后, 会将在任务队列放一个任务. 浏览器渲染主线程有空了就会来取.
渲染主线程拿到任务后会逐步解析 HTML, 生成一个 DOM 树, 同时会开启一个预加载扫描器, 这个预加载扫描器跑的比渲染主线程快, 遇到一个静态资源就会发起一个网络请求.
遇到的是媒体资源如视频, 图片等, 不会阻塞 HTML 解析也不会阻塞渲染
遇到的是 css 资源, 不会阻塞 HTML 解析, 但是会阻塞渲染
遇到的是 js 资源, 会阻塞 HTML 解析, 也会阻塞渲染. 因为这里可能会改变 DOM 树
这个预加载扫描器遇到 css 会自己解析 css, 然后将解析结果交给主线程, 主线程再处理一下就可以用了.
在完成这一步后就会得到 DOM 树和 CSSOM 树了.
样式计算
主线程会遍历得到 DOM 树, 为每一个节点计算出最终的样式, 这一步就是样式计算(Computed Style)
在这个过程中, 之前很多预设值会转变成绝对值, 如color: red;
会转变成rgb(255,0,0)
.
这一步完成后就得到了带有样式的 DOM 树了.
布局
布局阶段会遍历刚刚得到的 DOM 树, 然后为每个节点计算出几何信息, 如节点的宽高、相对包含块的位置, 生成一棵布局树.
布局树和 DOM 树不是一一对应的, 有些 DOM 节点可能是display: none;
的, 所以布局树里没有; 有些文本可能是没有被行盒包裹, 直接被块盒包裹的, 会生成一个匿名行盒. 同时一些伪元素如 before, after 里也会生成节点.
分层
我们的页面是有交互的, 有交互就有可能带来页面的变化. 重新渲染整个页面不太可能, 浏览器会进行分层, 某一个东西改变了只会重新渲染它所在的一层.
分层也不是越多越好, 会占用内存空间. 只能说是空间换时间吧.
可以使用will-change
来影响分层结果.
绘制
这个过程就跟我们使用 canvas 一样, 命令画笔从哪个点开始, 做什么.
主线程会为每个层单独产生绘制指令集, 用于描述这一层的内容该如何画出来.
完成绘制后, 主线程会把这些信息交给合成线程.
分块
合成线程会对每个图层进行分块, 它会从线程池里拿出更多线程来完成分块.
光栅化
分块完成后合成线程会将块信息交给 GPU 进程, 这个阶段会执行的非常快.
GPU 会分出很多线程来完成光栅化, 并且优先处理靠近视口区域的块.
光栅化的结果, 就是一块一块的位图.
画!
合成线程拿到位图后, 会生成一个个的指引信息, 指示哪一个位图应该画在屏幕上的哪一个地方, 这里还会考虑旋转, 缩放等变形.
变形发生在合成线程这里, 跟渲染主线程无关, 因此transform
的效率很高.
合成线程会把指引提交给 GPU 进程, 由 GPU 进程产生系统调用, 提交给 GPU 硬件, 完成最终的屏幕成像.