浏览器访问网页全流程:小白友好版详解
1. 输入网址:从"网址"到"网络请求"的起点
- 你要做的事 :在浏览器地址栏输入网址(比如
https://www.baidu.com
),按回车。 - 浏览器的"小心思" :
浏览器首先会检查你输入的是不是"网址"(URL),比如有没有http://
或https://
。如果没有,它可能会自动补全(比如你输入baidu.com
,它会默认加https://
)。
接着,浏览器会启动一个"网络请求线程"------你可以理解为浏览器派了一个"快递员",负责把你的请求送出去,并把网页"包裹"取回来。
类比:你在购物APP上输入商品名称,APP会启动一个"订单处理员"帮你下单。
2. 找服务器:DNS查询------给"网址"找个"门牌号"
-
核心问题 :网址是给人看的(比如
baidu.com
),但电脑只认"IP地址"(类似服务器的"门牌号",比如14.215.177.38
)。浏览器需要通过DNS查询把网址翻译成IP地址。 -
DNS查询的"快递路线" (从近到远找,节省时间):
- 浏览器缓存 :浏览器记不记得最近查过这个网址?(比如你刚访问过
baidu.com
,浏览器暂时存了它的IP)。 - 本机缓存:你的电脑有没有存?(比如操作系统自己记了一些常用网址的IP)。
- hosts文件:一个特殊文件,相当于"本地通讯录",可以手动写死网址和IP的对应关系(一般不用管)。
- 路由器缓存:家里的路由器有没有存?(路由器帮全家设备查过的网址,可能会暂时记住)。
- ISP DNS缓存:你的网络服务商(比如联通、电信)的"DNS服务器"有没有存?(这是专业的"地址簿",存了海量网址的IP)。
- DNS递归查询 :如果以上都没有,ISP的DNS服务器会帮你去"根服务器"(全球只有13台!)一层层查,直到找到对应的IP。
类比:你不知道"XX超市"的地址,先问家人(浏览器缓存),再查手机备忘录(本机缓存),再问小区保安(路由器),最后问114查号台(ISP DNS),直到找到地址。
- 浏览器缓存 :浏览器记不记得最近查过这个网址?(比如你刚访问过
3. 建立连接:TCP三次握手------"喂,你在吗?我能发东西吗?"
-
核心问题 :找到IP地址后,浏览器需要和服务器建立"可靠的连接",才能开始传数据。这个过程叫TCP三次握手,确保双方都能正常收发消息。
-
三次握手"对话" (想象你和服务器打电话确认信号):
- 客户端(你) :"喂,服务器,我能给你发数据吗?"(发一个
SYN=1
的包,相当于"请求连接")。 - 服务器 :"收到!我能收到你,你也能收到我吗?"(回一个
SYN=1, ACK=客户端序号+1
的包,相当于"确认收到,并反问你")。 - 客户端(你) :"收到!我也能收到你,开始发吧!"(发一个
ACK=服务器序号+1
的包,相当于"确认可以开始")。
为什么要三次? 防止"过时的连接请求"干扰。比如你发了一个请求,但网络延迟,服务器很久才收到,此时你可能已经不想连接了,但服务器以为你要连,就会浪费资源。三次握手能确保双方都是"当前在线且愿意连接"的。
类比:打电话时,你说"喂?"(1次),对方说"喂,能听到吗?"(2次),你说"能听到,开始说吧"(3次),确认双方都能正常通话。
- 客户端(你) :"喂,服务器,我能给你发数据吗?"(发一个
4. 发请求:HTTP请求------"我想要这个网页,请给我!"
-
连接建立后 ,浏览器(客户端)会通过TCP连接给服务器发一个HTTP请求报文(相当于"订单详情")。
-
HTTP请求报文的"快递单"内容:
- 请求行 :方法(一般是
GET
,表示"获取资源")、网址路径(比如/index.html
)、HTTP版本(比如HTTP/1.1
)。 - 请求头 :附加信息,比如"我是Chrome浏览器"(
User-Agent: Chrome
)、"我能接受gzip压缩的内容"(Accept-Encoding: gzip
)、"我有没有缓存过这个网页"(If-Modified-Since
或If-None-Match
,后面讲缓存会细说)。 - 请求体 :如果是提交数据(比如登录表单),这里会放数据;如果是
GET
请求,这里为空。
类比:你给超市发订单,写着"买1瓶可乐(请求行),我用的是XX快递(请求头),地址是XXX(请求体)"。
- 请求行 :方法(一般是
5. 服务器处理请求:从"接收订单"到"准备包裹"
-
服务器收到请求后,会做几件事:
- 解析请求 :服务器的"前台"(比如Nginx、Apache)先看请求头里的
Host
字段(比如www.baidu.com
),判断你要访问哪个"虚拟主机"(一个服务器可能同时放多个网站,比如baidu.com
和tieba.baidu.com
)。 - 缓存验证 :服务器会检查请求头里的缓存信息(比如
If-Modified-Since
):"这个网页客户端是不是已经缓存过了?如果没过期,就不用重发了,让它用自己的缓存吧。"如果缓存有效,服务器会返回304状态码("网页没变化,用你自己的缓存吧")。 - 处理业务 :如果需要新的网页,服务器会让"后台程序"(比如Java、Python代码)处理,可能需要查数据库(比如"用户信息""商品列表"),然后生成HTML、CSS、JS等内容。
类比:超市收到订单后,先看你是不是会员(Host),再看你要的商品是不是刚买过且没过期(缓存验证),如果需要新商品,就去仓库(数据库)拿货,打包成包裹(HTTP响应)。
- 解析请求 :服务器的"前台"(比如Nginx、Apache)先看请求头里的
6. 收响应:HTTP响应------"你的网页包裹到了!"
-
服务器把网页内容打包成HTTP响应报文**,通过TCP连接发回给浏览器。响应报文包含:
- 状态码 :服务器的"回复",比如
200 OK
(成功)、404 Not Found
(网页不存在)、500 Internal Server Error
(服务器出错了)。 - 响应头 :比如"内容类型是HTML"(
Content-Type: text/html
)、"内容大小是10KB"(Content-Length: 10240
)、"缓存有效期是1小时"(Cache-Control: max-age=3600
)。 - 响应体 :网页的实际内容,比如HTML代码、图片二进制数据等。
类比:超市把可乐(响应体)装进盒子,贴上面单(响应头:"易碎品""保质期1小时"),写上"发货成功"(状态码200),然后让快递员送回你家。
- 状态码 :服务器的"回复",比如
7. 关闭连接:TCP四次挥手------"东西收到了,再见!"
-
数据传完后 ,如果浏览器和服务器不再需要通信,就会关闭TCP连接,这个过程叫四次挥手。
-
四次挥手"对话" (比三次握手多一次,确保双方都发完数据):
- 主动方(比如浏览器) :"我说完了,你还有要说的吗?"(发
FIN=1
,表示"我要关闭发送通道了")。 - 被动方(服务器) :"收到,我知道你说完了,但我可能还有数据没发完,等我一下。"(发
ACK
确认)。 - 被动方(服务器) :"我也说完了,你收到了吗?"(发
FIN=1
,表示"我也关闭发送通道了")。 - 主动方(浏览器) :"收到,再见!"(发
ACK
确认,双方关闭连接)。
类比:打电话结束时,你说"我没话说了"(1次),对方说"好的,我知道了,你等我说完最后一句"(2次),对方说"我也说完了"(3次),你说"好,挂了"(4次)。
- 主动方(比如浏览器) :"我说完了,你还有要说的吗?"(发
8. 缓存:"这次的包裹先存着,下次直接用!"
-
缓存的作用:如果网页内容没变,浏览器不用每次都去服务器下载,直接用本地缓存,能节省时间和流量。
-
缓存怎么判断"要不要用"? 服务器在响应头里会告诉浏览器:
- Expires (HTTP 1.0):一个绝对时间,比如
Expires: 2024-01-01 12:00:00
,表示"2024年1月1日12点前,缓存有效,直接用"。 - Cache-Control: max-age=3600(HTTP 1.1,更常用):相对时间,"从现在开始1小时内(3600秒),缓存有效"。
- 如果缓存过期了 :浏览器会发请求给服务器"验证缓存",比如带
If-Modified-Since
("这个网页最后修改时间是不是比我缓存的新?"),如果服务器说"没变",就返回304状态码,浏览器继续用缓存;如果变了,就返回新内容(200状态码)。
类比:你买了一瓶可乐没喝完,放进冰箱(缓存),下次想喝直接拿,不用再去超市买。
- Expires (HTTP 1.0):一个绝对时间,比如
9. 解析渲染:把"代码"变成"网页"------浏览器的"魔法时刻"
-
浏览器拿到响应体(HTML/CSS/JS代码)后,需要把代码变成你看到的网页,这个过程叫"解析渲染",分几步:
-
构建DOM树 :HTML代码是"标签嵌套"的(比如
<html><body><p>你好</p></body></html>
),浏览器会把标签解析成"树状结构"(DOM树),每个标签是一个节点,方便后续操作。- 类比:把"宜家家具说明书"(HTML代码)拆解成"零件列表"(DOM节点)。
-
构建CSSOM树 :CSS代码(比如
p { color: red; font-size: 16px; }
)会被解析成"CSS规则树"(CSSOM),记录每个标签的样式。- 类比:把"给零件上色的说明"(CSS代码)整理成"每个零件的颜色和大小表"(CSSOM)。
-
构建渲染树(Render Tree) :把DOM树和CSSOM树合并,只保留"可见节点"(比如
<script>
标签、display: none
的元素不会显示,就不进渲染树),并计算每个节点的位置、大小、颜色。- 类比:把"零件列表"和"上色说明"合并,得到"最终组装图"(渲染树),明确每个零件的位置和样子。
-
布局(Layout) :计算每个节点在屏幕上的精确位置(比如"这个段落左上角在(100px, 200px),宽200px,高50px")。
-
绘制(Painting) :根据渲染树和布局结果,把像素画到屏幕上(比如给文字上色、画边框、显示图片)。
-
复合(Composite) :如果页面有多层(比如固定在顶部的导航栏、滚动的内容区),浏览器会把不同层的绘制结果合并,最终显示完整网页。
-
10. JS解析:让网页"动起来"的"脚本"
-
HTML解析时遇到
<script>
标签 ,浏览器会暂停HTML解析,转而去解析JS代码------因为JS可能会修改DOM(比如document.write()
)或CSS(比如element.style.color = 'red'
),如果不暂停,可能会导致渲染错误。 -
JS解析的"三步曲" :
- 词法分析 :把JS代码拆成"单词"(比如
var a = 1
拆成var
、a
、=
、1
)。 - 语法分析 :把"单词"组成"语法树"(比如判断
var a = 1
是不是合法的JS语法)。 - 执行:JS引擎(比如Chrome的V8引擎)会创建"执行上下文"(记录变量、函数、作用域等),然后一行行执行代码。
- 词法分析 :把JS代码拆成"单词"(比如
-
JS执行的"小细节" :
- 同步脚本 (没有
async
/defer
):会阻塞HTML解析,必须等JS下载+执行完,才能继续解析HTML。 - 异步脚本(
async
) :下载时不阻塞HTML解析,下载完后立刻执行(顺序不确定,谁先下载完谁先执行)。 - 延迟脚本(
defer
) :下载时不阻塞HTML解析,等HTML解析完后,按代码顺序执行(适合需要操作DOM的脚本)。
类比 :你在拼乐高(HTML解析),突然发现说明书里夹了一张"补充说明"(JS代码),你必须先看完补充说明(解析JS),才能继续拼(解析HTML)。如果补充说明是"先放一边,等拼完主体再看"(defer
),就可以先继续拼主体。
- 同步脚本 (没有
11. 页面显示:"逐步渲染"------看到的网页不是"一下子"出来的
-
浏览器解析HTML时是"边解析边渲染"的 :比如解析到
<p>你好</p>
,就会立刻把这个段落画到屏幕上,不用等整个HTML都解析完。这就是为什么有些网页加载时,你会看到内容"一点点出来"。 -
两个重要事件:
- DOMContentLoaded:HTML解析完,DOM树构建完成(此时JS可能还没执行完,图片可能还没加载完)。
- load:所有资源(HTML、CSS、JS、图片、视频等)都加载完,网页完全显示。
总结:从输入网址到看到网页的"全流程"
- 输入网址 → 浏览器启动网络线程。
- DNS查询 → 把网址翻译成IP地址。
- TCP三次握手 → 和服务器建立可靠连接。
- 发HTTP请求 → 告诉服务器"我要什么网页"。
- 服务器处理 → 生成HTTP响应(网页内容)。
- 收HTTP响应 → 浏览器拿到网页代码。
- TCP四次挥手 → 关闭连接(如果不需要继续通信)。
- 缓存 → 保存网页,下次直接用(如果没过期)。
- 解析渲染 → HTML→DOM树,CSS→CSSOM树→渲染树→布局→绘制→显示网页。
- JS解析执行 → 让网页交互、动态变化。
整个过程就像"你点外卖":输入菜名(网址)→ 平台查餐厅地址(DNS)→ 骑手联系餐厅(三次握手)→ 下单(HTTP请求)→ 餐厅做菜(服务器处理)→ 骑手送餐(HTTP响应)→ 骑手确认收货后离开(四次挥手)→ 你打开外卖(解析渲染)→ 吃到嘴里(看到网页)。