前言
“从输入 URL 到页面展示发生了什么?”这不仅是前端面试的“常青树”,更是衡量一个开发者对浏览器原理、网络协议及渲染引擎掌握程度的试金石。本文将把这一过程拆解为 4 个关键阶段,带你走完这趟“字节之旅”
一、 导航阶段:地址栏的“变身术”
当你在地址栏敲下回车时,浏览器并不会立即跳转,而是先进行一番预处理。
-
输入处理:
- 浏览器判断输入的是 查询关键字 还是 URL。
- 如果是关键字,址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URLL。
- 如果是域名(如
baidu.com),自动补全协议为https://www.baidu.com/。
-
卸载当前页面:
- 触发
beforeunload事件。页面可以利用这个机会清理数据或弹窗询问用户是否离开。
- 触发
-
提交导航:
- 浏览器接收到url之后,浏览器进程会通过进程间通信把URL请求发送到网络进程,网络进程接收到 URL 请求后,会发起 URL 请求流程。
二、 连接阶段:寻路与握手
网络进程接收到请求后,开始执行真正的网络传输。
1. DNS 解析(寻找 IP 地址)
浏览器需要通过域名找到服务器的 IP 地址:
-
多级缓存查找:浏览器缓存 → 操作系统缓存(Hosts)→ 路由器缓存 → ISP(运营商)DNS 缓存。
-
查询方式:
- 递归查询:你问本地域名服务器,它负责跑腿帮你问到底。
- 迭代查询:本地域名服务器自己去问根、顶级、权威服务器,每次得到一个“下一跳”的指引。
2. TCP 三次握手
得到服务器ip地址后,接下来就需要与服务器的进行tcp连接,连接是通过3次握手实现的。
- 第一次握手:客户端发送
SYN包(seq=x)。 - 第二次握手:服务端返回
SYN+ACK包(seq=y, ack=x+1)。 - 第三次握手:客户端发送
ACK包(ack=y+1)。
补充:具体的三次握手与四次握手机制具体机制会在后续单独文章中讲解。
三、 HTTP 请求与响应阶段
建立TCP连接后就会准备发起http请求。
-
发起请求:这时浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。
-
缓存检查:浏览器发送请求时会先在浏览器缓存中查找缓存标识,从而判断是直接返回请求结果还是重新像服务器发送http请求。
- 强缓存:检查
Cache-Control(max-age)。命中则直接从本地读取,状态码 200。 - 协商缓存:若强缓存失效,携带
If-None-Match(Etag) 或If-Modified-Since(Last-Modified) 询问服务器。若资源未变,返回 304。
- 强缓存:检查
-
服务器处理:服务器接受到http请求后会对其进行解析,服务器中程序会检查请求头中协商缓存标识,从而判断是直接使用缓存文件还是根据请求重新查找对应数据并返回
-
链接是否关闭:浏览器接收http响应后,根据情况可以选择关闭tcp连接或者保留tcp连接继续使用。
补充:具体的三次握手与四次握手机制具体机制会在后续单独文章中讲解。
-
响应处理:浏览根据响应报文中的状态码,来进行不同的操作,
1xx、3xx、4xx、5xx与2xx的处理情况不同- 如果返回的状态码是 301 或者 302,那么说明服务器需要浏览器重定向到其他 URL。这时网络进程会从响应头的
Location字段里面读取重定向的地址,然后再发起新的 HTTP 或者 HTTPS 请求,一切又重头开始了 - 如果返回的状态码是 200,那么表示浏览器可以继续处理该请求
- 如果返回的状态码是 301 或者 302,那么说明服务器需要浏览器重定向到其他 URL。这时网络进程会从响应头的
-
客户端缓存处理:浏览器根据服务器返回的相关缓存标识判断资源是否可缓存
-
资源处理:接下来浏览器会根据响应结果中的
Content-Type字段来判断返回的资源类型,并决定如何处理:- text/html:说明服务器返回的数据是
HTML格式,接下来浏览器会准备渲染进程,开始渲染页面 - application/octet-stream:说明服务器返回的数据是字节流类型的,接下来浏览器会按照下载类型来处理该请求
- text/html:说明服务器返回的数据是
-
渲染进程准备:对于HTML文件,浏览器会准备渲染进程,而浏览器渲染进程策略如下:
- 通常情况下,打开新的页面都会使用单独的渲染进程
- 如果从 A 页面打开 B 页面,且 A 和 B 都属于同一站点的话,那么 B 页面复用 A 页面的渲染进程
补充: 同一站点是指根域名(
baid.com,不带www.)、协议(http://)相同,以及根域名下的所有子域名和不同的端口。例如下面例子都属于同一站点https://time.geekbang.org https://www.geekbang.org https://www.geekbang.org:8080
四、 渲染阶段:从代码到像素
渲染进程收到 HTML 后,开始“点石成金”,浏览器会将网络进程中的HTML文件,提交给渲染进程。
- 浏览器会对
HTML进行一个预解析,识别出是否有CSS和js文件需要下载,如果有的话就会同时发起对这两个文件的下载(下载时间按照最久的那个文件来算)
1. 构建树结构
接着浏览器会自上而下解析html文件,根据解析的html生成dom树,在构建dom树的同时会解析css并生成css样式表
-
DOM 树:首先根据html语义规范将字符流转换为标记,然后再将这些标记转换为对象并定义属性和规则,最后根据标记关系将对象组成dom树。
-
CSS 树:首先将 CSS 文本转换为浏览器可以理解的样式表(styleSheets)结构,接下来会对样式表中的属性值进行标准化操作,将将样式表中所有值转换为渲染引擎能够理解的标准化计算值。
-
布局树 (Layout Tree) :合并 DOM 和 CSSOM,过滤掉
display: none的节点,计算几何位置。补充: 在解析html过程中如果遇到外联资源则会去下载这些资源,如果是阻塞型的外联资源(例如:内联js代码、外联js代码、内联css代码),这时会停止对于dom的解析,如果是非阻塞型资(外联async属性和defer属性的js文件)源则不会影响对于dom的解析。
2. 分层与分块
接下来渲染进程会对布局树进行分层,生成图层树,因为页面由一层一层图层组成。
- 分层 (Layering) :为了处理 3D 转换、Z-index、裁剪(overflow),浏览器生成图层树 (Layer Tree) 。
- 分块 (Tiling) :合成线程将图层划分为小的图块。
- 在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制,生成绘制列表,在绘制操作中可能会遇到回流和重绘
3. 栅格化与显示
- 栅格化 (Raster) :将图块转换为位图。现代浏览器通常使用 GPU 加速(快速转换)。
- 合成与显示:合成线程发送
DrawQuad指令给浏览器进程,最后由 GPU 将页面内容绘制到显示器。 - 这时浏览器就会显示出页面了!
五、 补充:脚本加载策略 (defer vs async)
| 属性 | 加载顺序 | 执行时机 | 是否阻塞解析 |
|---|---|---|---|
| 无 | 立即下载 | 下载完立即执行 | 阻塞 |
| defer | 并行下载 | HTML 解析完成后,按顺序执行 | 不阻塞 |
| async | 并行下载 | 下载完立即执行(可能乱序) | 执行时阻塞 |