ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

前端必学计算机网络:从TCP到HTTP再到缓存与跨域排查

前端必学计算机网络:从TCP到HTTP再到缓存与跨域排查

先从一个特别真实的场景说起:你正在前端页面里调一个接口,线上反馈“点了按钮没反应”,打开控制台一看,请求是红色的,状态码 502,后端说服务正常,监控也没报警。紧接着又有人反馈白屏、图片加载失败、接口超时。这时候如果你脑子里只有“不会吧”三个字,那就非常被动了。

我印象很深的一次,某系统上线后首页图片资源一直加载不出来,排查了半天,最后发现是 CDN 域名解析的问题,跟后端接口一点关系都没有。如果你没有计算机网络的基本功,这类问题摆在你面前就是玄学。这篇文章就是从一个前端开发者的视角,把计算机网络重新捋一遍:从分层模型到 TCP、从 HTTP 到 WebSocket、从跨域到缓存,再到日常问题排查。不写教科书式的定义堆砌,重点讲每个知识点到底跟你的实际开发有什么关系、你什么时候用得上。

很适合正在补基础的前端、天天跟接口和页面性能打交道但没系统学过网络的人。这篇文章信息密度不低,建议分几次看,别指望一次性全消化,看完之后再去翻抓包工具里的请求列表,你会突然看明白很多东西。

1. 铺垫:前端为什么非要死磕计算机网络

1.1 这不是面试题,是日常排查的武器

一说前端学网络,很多人第一反应是“面试要背三次握手四次挥手”。确实,面试会问,但你要是只为了背八股而学,那这波亏大了。前端开发写页面、调接口、引 SDK、做性能优化,每一项都踩在网络协议上。

举个最简单的例子:你的页面加载了一个 2MB 的图片,用户反馈很慢。优化手段不外乎压缩图片、上 CDN、加缓存、延迟加载。可你有没有想过,2MB 的文件从服务器传到你手机上,具体要经过哪些环节?是哪一段慢?如果是跨运营商网络,为什么晚高峰特别明显?如果你连 DNS、TCP、HTTP、CDN 这几层的关系都不清楚,就只能靠猜。

再比如项目里常见的跨域报错。前后端分离之后,几乎所有前端都遇到过No 'Access-Control-Allow-Origin' header is present。很多人一看到这个就以为是后端配置问题,可后端同事明明已经加了跨域头,为什么还是报错?这就涉及预检请求、简单请求、认证请求这些网络层面的机制。没有这套知识,你连问题边界都划不清楚。

所以前端学计算机网络,不是为了背概念,是为了给自己装一套“网络感知能力”。看到状态码能猜出大概方向,看到瀑布图能判断瓶颈在哪个阶段,看到接口失败能快速分清是 DNS、TCP、TLS、HTTP 还是业务代码的问题。这套能力,才是在项目里能独当一面的底气。

1.2 你需要掌握的核心知识地图

把计算机网络拆开看,前端真正高频用到的知识其实非常聚焦,不需要像网络工程师一样深挖路由协议和交换机原理。按重要程度和个人经验,前端必须掌握的有这么几块:

  • 分层模型:OSI 七层、TCP/IP 四层,先搞清楚网络世界里谁负责干什么。
  • TCP 与 UDP:面向连接的可靠传输和无连接的实时传输,这是 HTTP、WebSocket、流媒体等所有上层应用的底座。
  • HTTP/HTTPS:基本报文结构、状态码、缓存、跨域、版本演进,前端 90% 的日常都在这层。
  • DNS:从域名到 IP 的解析过程,直接影响页面首次加载速度。
  • WebSocket:从“请求-响应”到“双向实时”的转变,长连接怎么建立、怎么保活。
  • 网络安全相关:HTTPS 握手、同源策略、CORS、CSRF 防护,跟前端安全直接相关。
  • 性能优化中的网络部分:CDN、HTTP/2、连接复用、资源加载策略。

下面我按照一条“从底层到上层、从原理到实战”的顺序,把每一块讲透。

2. 分层模型:把网络切块理解

2.1 OSI 七层模型:教科书式的解剖图

OSI 参考模型把网络通信分成七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。对前端来说,不需要背每一层的细节,但要有“分层”这个心智模型。

我习惯用一个送快递的类比来理解。你网购了一个杯子,商家把杯子放进纸箱,在箱子上贴快递单,快递网点用卡车把箱子运到你所在的城市,快递员派送到你家门口,你拆开纸箱看到杯子。这里的“装箱”“贴单”“运输”“派送”“拆箱”就是不同层的职责。

在计算机网络里,应用层是你写的 HTTP 请求、WebSocket 数据帧;传输层负责把数据分成合适的段,标好源端口和目标端口,确保数据可靠或及时地送达;网络层负责寻址和路由,相当于快递干线运输的路线规划;数据链路层和物理层负责具体的帧传输和信号收发。

这套分层最大的价值,是让每一层只关心自己的职责,上层不需要知道数据是怎么经过网线、无线信号传过去的,下层也不需要理解 HTTP 报文里写了什么。前端做网络排查的时候,脑子里有这个分层图,就不会病急乱投医。

2.2 TCP/IP 四层模型:实际互联网的分层方式

OSI 七层模型是理想化的参考模型,实际互联网用的是 TCP/IP 四层模型,或者说是五层混合模型:应用层、传输层、网络层、数据链路层、物理层。

对应关系很简单:OSI 的会话层、表示层、应用层在 TCP/IP 里都被合并成了应用层。也就是说,HTTP、DNS、WebSocket、FTP、SSH 这些协议的实现细节,在分层模型里都算应用层;而 TLS/SSL 通常被放在应用层和传输层之间,因为它是对上层数据做加密,再交给 TCP 传输的。

前端写接口、发请求,绝大部分时候都是在应用层工作。但你发出去的数据,会一路往下被层层封装:应用层生成 HTTP 报文,传输层加上 TCP 头变成报文段,网络层加上 IP 头变成数据报,数据链路层再加上 MAC 头变成数据帧,最终变成比特流在物理介质上传输。接收方再反向拆包。

这就是为什么抓包工具里你能看到“请求头、响应头、TCP 连接信息、TLS 握手详情”这些东西,因为它们本来就分布在不同的层。用浏览器开发者工具看请求时,网上随便找“Stalled 时间很长”“TTFB 很高”“Content Download 很慢”,这些指标对应的其实就是不同层的耗时。

2.3 前端最常接触的两层:应用层与传输层

前端开发者的工作重心,几乎全在应用层和传输层。应用层是你每天写的 HTTP 请求、设置的 Cookie、调试的接口鉴权;传输层则是你是否能建立连接、数据是否可靠、有没有丢包重传。

我建议前端理解一个关键点:TCP 是面向字节流的可靠协议,它会保证你请求的所有字节都完整、有序地到达服务器。为了做到这一点,TCP 做了很多事:分包、编号、校验、确认、重传、流量控制、拥塞控制。而 UDP 是面向报文的不可靠协议,它只负责把数据发出去,不保证到达。

为什么这个区别很重要?因为前端所有基于 HTTP 的请求都依赖 TCP 的可靠性,而基于 WebSocket 的实时通信虽然底层是 TCP,但它的消息边界是在应用层自己处理的。做直播、游戏、音视频通话时,往往会选择 UDP 或基于 UDP 的传输协议,因为延迟比可靠更重要。理解了这一层,你在做技术选型时就不容易犯糊涂。

3. TCP 与 UDP:传输层的两副面孔

3.1 TCP 三次握手:确认双方都在线

TCP 连接建立需要三次握手,这个不用说,所有前端都被问过。但我更想讲清楚的是,三次握手到底在解决什么问题。

假设你在微信上跟一个朋友语音通话,接通前你肯定要确认对方能听见。你问一句“能听到吗?”对方回答“能听到,你能听到我吗?”你再回一句“能听到。”这三次说话,就同时确认了两件事:你的发送能力和接收能力都正常,对方的发送能力和接收能力也正常。

TCP 三次握手一模一样:

  • 第一次:客户端发送 SYN 报文,告诉服务器“我想建立连接,我的初始序号是 X”。
  • 第二次:服务器回应 SYN + ACK,告诉客户端“收到你的请求,我的初始序号是 Y,同时确认你的序号已收到”。
  • 第三次:客户端发送 ACK,告诉服务器“我收到了你的确认,连接正式建立”。

如果只有两次握手,服务器无法确定客户端是否已经收到自己的响应,可能造成连接资源的白白浪费。如果四次握手,第三步的 ACK 之后再加一步就冗余了。所以三次握手不是拍脑袋定的,是可靠性和效率的平衡点。

前端实际开发中,你会反复看到“三次握手耗时”这个概念。在 Chrome 的 Network 面板里,瀑布图的Stalled、Initial connection阶段其实就包含了 TCP 握手和 TLS 握手的时间。如果你的接口请求数量特别多,每次都重新建立 TCP 连接,光握手开销就非常可观。这也是 HTTP/1.1 引入 Keep-Alive、HTTP/2 引入多路复用的根本原因——省掉没必要的握手。

3.2 四次挥手:体面地告别

断开连接要比建立连接多一次,因为 TCP 是全双工的,两个方向都可以独立地发送数据。客户端想断开连接,并不是说它的接收通道也立刻关掉了,而是要等双方都确认“我不再发数据了,你也别再发了”,才能真正释放连接。

过程是这样:

  1. 客户端发送 FIN,告诉服务器“我这边没有数据要发了”。
  2. 服务器回 ACK,说“知道了,我准备好关闭接收了”,但服务器可能还有数据没发完。
  3. 服务器把剩余数据发完后,发送 FIN,说“我这边也没有数据了”。
  4. 客户端回 ACK,然后经过一段等待时间后关闭连接,服务器收到 ACK 后也关闭。

四次挥手里最容易被忽略的状态是TIME_WAIT。主动关闭方在收到对方的 FIN 后并不会立刻关闭,而是要等 2 个最大报文段生存时间(MSL),这是为了确保最后一个 ACK 能被对方收到,同时让网络中残留的延迟数据包彻底消失,避免影响下一个使用相同端口的新连接。

前端在开发环境或线上健康检查里,经常会看到大量TIME_WAIT状态。如果你的服务端需要主动断开大量短连接,却没开长连接和连接池,就会出现端口耗尽、连接被拒的情况。虽然这通常属于服务端运维问题,但前端要懂机制,否则后端让你查连接数,你连状态都看不懂。

3.3 TCP 拥塞控制与前端关心的延迟

TCP 的拥塞控制机制,是网络拥堵时“自动减速”,网络通畅时“试探提速”的一整套策略。里面有几个名词面试常考:慢启动、拥塞避免、快重传、快恢复。

慢启动的意思是,连接建立初期发送方不知道网络带宽有多少,所以先发一小部分数据试探,收到确认后指数增加发送量,直到达到拥塞阈值或者发生丢包再降低。整个过程像你在窄路上开车,不确定路况时先慢慢开,确认没坑了再踩油门。

前端的感知是什么?一个页面首次连接服务器时,TCP 的慢启动会让前几个请求的数据包不会一下子打满带宽。如果一个页面依赖一个非常大的并行加载任务,第一次握手的连接往往比复用连接慢。这也是为什么优化静态资源时,把网站核心资源放在同一个域名下、利用 HTTP/2 多路复用,可以显著加快首屏速度。TCP 还没跑到最高速度窗口时,资源已经被接收完了,那整体时间就短。

丢包后的重传和拥塞窗口缩减,则决定了弱网环境下请求表现得有多“卡”。前端做弱网模拟时,能看到请求被拖得很长、文件下载半路卡住,这就是 TCP 在反复重传。理解这个过程,你就知道不能只靠后端优化,前端也应该设计好错误重试、渐进加载和超时处理。

3.4 UDP 为什么重要:实时通信的底座

很多人以为 UDP 不重要,因为前端的 HTTP 请求走的是 TCP。但你打音视频电话、看直播、玩在线游戏时,底层几乎都是 UDP 或者基于 UDP 的实现。为什么?

因为实时场景里“等不起”。TCP 的可靠传输靠确认重传保证,但这意味着一旦丢包,数据要在超时后才重传,对实时音视频来说,等重传的数据到了已经晚了。丢一帧画面,不如继续播后面的新帧。UDP 不做可靠保证,发送方只管发,接收方来不及处理就丢,用户体验反而是流畅的。

对前端来说,如果你要做的项目涉及 WebRTC 点对点直播、语音通话、屏幕共享,那么 UDP 不是冷门知识,而是底座。WebRTC 的数据通道、媒体传输大量使用 UDP,还要处理丢包补偿、抖动缓冲。你不需要自己实现 UDP 协议,但要知道为什么不用 TCP 做实时通话,为什么弱网下画面会花,声音会断续。

4. HTTP 协议:前端的命根子

4.1 HTTP 报文结构:请求和响应的骨架

前端每天都在跟 HTTP 打交道,但真正把报文结构讲清楚的人不多。HTTP 报文分三部分:起始行、头部字段、消息体。请求报文和响应报文的差别就在起始行。

请求的起始行长这样:

GET /api/user?id=123 HTTP/1.1 Host: example.com Origin: https://example.com

第一段是方法,第二段是 URI,第三段是协议版本。响应的起始行长这样:

HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Cache-Control: max-age=3600

头部字段由无数个键: 值组成,控制着缓存、跨域、认证、内容协商等行为。消息体是实际传输的数据,可以是一段 JSON、一张图片、一个文件流。

前端日常调试里最常用的就是看头部。比如Content-Type决定请求体的编码方式,这也是为什么POST请求要用Content-Type: application/json而不是表单编码;Set-Cookie决定浏览器如何种下 Cookie;Access-Control-Allow-Origin控制跨域访问;ETag、Last-Modified控制缓存验证。

报文的本质就是纯文本。你可以在 Node 里写一个简单的 HTTP 服务,然后手动拼一个响应报文发回去,浏览器照样能渲染。我以前给同事讲的时候,直接在终端里用curl -v看完整报文,比任何工具都直观——它把请求头、响应头、TLS 握手过程全部打给你看,前端一定要学会看这部分信息。

4.2 HTTP/1.1 到 HTTP/2 再到 HTTP/3:版本的演进逻辑

HTTP/1.1 是目前兼容性最好的版本,支持 Keep-Alive 长连接、支持分块传输、支持多种缓存策略。但它的最大问题是队头阻塞:同一个 TCP 连接上只能串行处理响应,前一个请求没返回,后一个请求就得等着。为了解决这个问题,浏览器对一个域名开了多个并发连接,但连接数有限,而且连接之间的资源竞争非常浪费。

HTTP/2 的核心突破是二进制分帧和多路复用。它把一个 TCP 连接切成多个流,每个流里传输不同请求的帧,接收方再按帧重组,于是多个请求可以同时在一个连接上跑,不再排队。同时 HTTP/2 还支持头部压缩和服务端推送,减少了大量重复的头部开销。

但 HTTP/2 依然有一个心病:TCP 层本身的队头阻塞。如果一个 TCP 包丢了,后续的数据即使已经到达接收缓冲,也要等重传的包补齐才能交给上层。HTTP/3 干脆换掉了传输层,改用 QUIC 协议,QUIC 基于 UDP 实现,自己在应用层做可靠性和拥塞控制,把队列阻塞控制在单个请求内。

前端的实际意义是:部署 CDN 时开启 HTTP/2 很容易,但 HTTP/3 需要服务端和浏览器的双重支持,不能盲目以为“升级版本就一定更快”。你要看目标用户群体的浏览器占比、服务端网络环境,有些内网客户端的网络策略可能直接拦掉 UDP 流量,导致 HTTP/3 降级失败。

4.3 HTTPS 加密过程:安全是怎么建立的

HTTP 是明文传输,用户密码、Cookie、敏感接口数据在中间任何一台路由器上都能被看到,所以现代网站几乎全量上了 HTTPS。HTTPS 的本质是 HTTP 加了一层 TLS 加密。

TLS 握手的过程可以简化成三步:协商加密套件、身份验证、生成会话密钥。

  1. 客户端发ClientHello,告诉服务器自己支持的 TLS 版本、加密套件列表、随机数。
  2. 服务器回ServerHello,选定加密套件和 TLS 版本,返回自己的证书,证书里包含公钥和域名信息,并完成一次签名验证。
  3. 浏览器验证证书链,确认证书可信、域名匹配、没有过期,然后生成一个预主密钥,用服务器的公钥加密发给服务器。双方再用预主密钥结合之前的随机数,各自计算出相同的会话密钥。之后所有数据都用这个会话密钥对称加密。

这里有前端常见的两个坑。

第一个坑是证书链不完整。有些内网自建证书,服务器只配了叶子证书,没有把中间证书链一起发给浏览器,导致浏览器无法验证证书可信,报NET::ERR_CERT_AUTHORITY_INVALID。你服务端配的证书“没问题”,但用户端就是不认。

第二个坑是 HSTS 和混合内容。如果页面在 HTTPS 下仍混用了 HTTP 的资源,浏览器会拦截并提示Mixed Content。这类报错经常出现在前端接第三方图片、外链脚本时。解决方式很简单,把所有静态资源统一到 HTTPS,或者在引用时用协议相对路径//。

TLS 握手也是性能成本的一部分。Initial connection里除了 TCP 三次握手,还要算上 TLS 握手的时间,所以开启 TLS 1.3 后因为握手往返次数减少,明显能感受到首屏更快。TLS 1.3 只保留 1-RTT 握手,配合 0-RTT 恢复,对移动端弱网特别有意义。

4.4 HTTP 缓存机制:减少重复加载的核心

前端做性能优化,缓存是最容易见效、也最容易做错的一环。HTTP 缓存可以分成强缓存和协商缓存两大类。

强缓存是浏览器直接从本地缓存读取资源,不发请求到服务器,由Cache-Control或Expires控制。Cache-Control: max-age=3600表示资源在 3600 秒内直接使用缓存,不再请求服务器。Expires是老的绝对时间写法,因为客户端时间不准,现在基本由Cache-Control取代。

协商缓存是资源过期后,浏览器带着缓存标识去服务器问“这个资源还能用吗”,服务器说可以则返回 304,不会重新下载正文,只返回响应头。协商缓存由ETag和Last-Modified控制。

前端最经典的坑是“HTML 不能强缓存,静态资源必须强缓存”。HTML 是入口,如果 HTML 被强缓存了,用户拿到的就是旧页面的引用,新发布的 JS/CSS 文件名变了也不会去拉新的。所以正常做法是 HTML 使用no-cache做协商缓存,JS/CSS/图片等带哈希指纹的文件使用max-age加immutable,长期强缓存。

另一个坑是缓存粒度太粗。一个文件同时被复用和更新时,如果文件名不变,只改内容,就要靠ETag或Last-Modified兜底。如果服务端设置Cache-Control: no-store,每次请求都完全不打缓存,性能就很差。实际项目里我建议后端灰度发布时,先观察缓存命中率指标,再决定要不要动缓存策略。

4.5 跨域与前端调试:同源策略的弯弯绕

同源策略是浏览器的安全基石。两个 URL 的协议、域名、端口只要有一个不同,就视为跨域。浏览器默认阻止跨域请求读取响应,但服务端可以通过 CORS 机制放行。

CORS 的核心是响应头:

  • Access-Control-Allow-Origin:允许哪个源访问。
  • Access-Control-Allow-Methods:允许哪些方法。
  • Access-Control-Allow-Headers:允许哪些自定义头部。
  • Access-Control-Allow-Credentials:是否允许携带 Cookie。

如果你发了一个非简单请求,比如Content-Type: application/json的 POST,浏览器会先发出一个OPTIONS预检请求,服务器确认允许后,才会发出真正的 POST。很多后端只配了Allow-Origin,没处理OPTIONS,前端就会看到“预检请求失败”。

前端最常见的困惑是:跨域报错明明存在,为什么直接在地址栏访问那个接口又能通?因为地址栏访问是“导航”请求,不是“跨域读取”,不受同源策略限制;而fetch/XMLHttpRequest是跨域读取,浏览器要校验 CORS 头。

再说说开发时的跨域方案。本地开发通常用开发服务器代理,也就是把/api前缀的请求转发到后端地址,这样浏览器看到的请求是同源的,不触发跨域。生产环境则用 Nginx 反向代理或接入网关统一处理。我曾经遇到过一个团队,前端代码里写死了一个跨域的完整 URL,后端配置也写了,但上线后被网关拦了,因为没把自定义头加入白名单。跨域排查,一定要分层看:浏览器直接发起?经过代理?还是网关层又被处理了一遍?

5. DNS 解析:把域名变成 IP 的那一跳

5.1 DNS 查询过程:浏览器怎么找到服务器

当你在地址栏输入一个域名,浏览器并不是先把请求发到服务器,而是要先把域名解析成 IP 地址。整个过程涉及浏览器缓存、操作系统缓存、路由器缓存以及各级 DNS 服务器。

实际流程是这样的:浏览器先查自己的 DNS 缓存,命中就直接用;没命中就查操作系统的 hosts 文件和系统缓存;还没命中就发给配置的本地 DNS 服务器(通常是你接入的运营商分配的),本地 DNS 服务器递归查询根域名服务器、顶级域名服务器、权威域名服务器,最后拿到 IP 返回给浏览器。

这里有个前端特别容易忽略的问题:DNS 解析也是耗时的一部分。如果你首屏加载的域名特别多,每个域名都要做一次 DNS 查询,累加起来就是一笔不小的开销。减少域名数量、用 HTTP/2 把资源合并在少量域名下,既能减少 DNS 查询次数,又能减少 TLS 握手次数。

DNS 解析的故障表现也经常迷惑人:接口时通时不通、部分用户访问很慢、图片偶尔加载失败。最典型的案例是某个用户本地 hosts 文件里还留着老 IP,而服务器的 IP 已经换了,于是他就一直访问不到。这种问题从代码层面完全看不出来,得学会用nslookup或dig去查解析结果。

5.2 DNS 优化:减少解析时间的手段

前端能做的最直接优化,就是在 HTML 里写上dns-prefetch和preconnect。dns-prefetch告诉浏览器提前解析某个域名,preconnect更进一步,提前建立 TCP 和 TLS 连接。比如第三方统计脚本、字体文件、CDN 资源,这些域名不是首要页面资源,但在渲染阶段一定会用到,提前解析能省下关键路径上的时间。

不过preconnect要谨慎用,因为它会消耗连接资源,用得过多可能适得其反。我的经验是:核心接口域名和主要 CDN 域名用preconnect,次要资源域名用dns-prefetch。

CDN 本身也是 DNS 优化的重要一环。CDN 服务商会根据用户的地理位置和运营商,把同一个域名解析到不同的节点 IP 上,让用户从最近的节点获取资源。这就是为什么同一个静态资源在不同地区访问到的 IP 不一样。前端排查 CDN 问题时,可以用接口返回的响应头里的Via、x-cache、age等字段判断是否命中了 CDN 缓存。

6. WebSocket:打破请求响应模式的实时通道

6.1 WebSocket 握手和数据帧

HTTP 是“一呼一应”,客户端不请求,服务器就不能主动发消息。但很多业务场景需要服务器主动推送,比如聊天消息、行情刷新、协作编辑。解决方案之一就是 WebSocket。

WebSocket 首先通过 HTTP 协议完成一次升级握手。客户端发送一个带有Upgrade: websocket头的 HTTP 请求,服务器返回101 Switching Protocols,之后连接就变成 WebSocket 长连接,双方可以在同一个 TCP 连接上双向发送数据帧,不必再走 HTTP 请求响应的格式。

爬虫或者前端调试时,你会注意到 WebSocket 的消息在开发者工具的 “Messages” 面板里显示,而不是普通的请求列表里。数据帧有类型区分:文本帧、二进制帧、关闭帧、Ping/Pong 帧。Ping/Pong 就是用来保活的:一方发 Ping,另一方回 Pong,连接就一直保持在活跃状态,避免中间设备因为空闲断掉。

前端接入 WebSocket 时有个常见坑:连接状态管理。WebSocket 的连接可能因为网络波动、服务器重启、代理超时等原因意外断开,但是断开的感知有明显的滞后。所以成熟的方案都要做心跳检测:定时发 Ping,如果一段时间内没收到 Pong,就当连接异常,主动重连。重连还要带递增退避,避免断线高峰期所有客户端同时拼命重连,把服务器冲垮。

6.2 短轮询、长轮询、WebSocket 和 SSE 怎么选

前端做实时功能不止 WebSocket 一条路,很多人上来就梭哈 WebSocket,其实有更合适的方案。

  • 短轮询:定时用 HTTP 请求问服务器“有没有新数据”。实现极简单,但多数的请求都是空响应,浪费带宽和连接。
  • 长轮询:客户端发请求,服务器先挂住,有数据再返回。比短轮询省请求,但每次连接都要重新建立 HTTP 连接,延迟还是偏高。
  • SSE(Server-Sent Events):基于 HTTP 的单向服务器推送,客户端用EventSource接口接收文本事件。简单轻量,但只支持文本,且浏览器连接数有限制。
  • WebSocket:双向实时通信,延迟低,场景覆盖广,但要自己处理心跳、重连、消息格式、分布式广播。

我接项目时的判断逻辑是:如果只是“服务器推送给客户端”,而且数据是文本,SSE 就够;如果要做双向交互、消息频次很高、或者要传二进制数据,就上 WebSocket。聊天室、多人协作是典型 WebSocket 场景;网页里的通知提醒、日志流,用 SSE 反而更省事。

7. 前端网络性能优化:从加载到渲染

7.1 网络加载时间分解:瀑布图到底在看什么

用 Chrome 打开性能面板,Network 里的请求瀑布图能精确到每个请求的每个阶段。很多人只看一个 Total 时间,其实中间的阶段能说明很多问题。

常见阶段包括:

  • Queueing:请求在队列里等排期,可能是浏览器并发连接数限制。
  • Stalled:请求迟迟没有发出,可能是等待代理协商、服务 worker 处理、磁盘缓存读写。
  • DNS Lookup:DNS 解析时间。
  • Initial connection:TCP 握手加 TLS 握手。
  • TTFB(Time To First Byte):服务器从收到请求到返回第一个字节的时间,包含后端处理时间、网络往返时间。
  • Content Download:下载响应体的时间,如果内容很大,会明显拉长。

排查页面慢,我会先看是哪个资源占了主要时间。如果 TTFB 极高,问题大概率在服务端或者网关,前端代码帮不上忙;如果 Content Download 很长,就要看是不是资源体积太大、CDN 带宽不够、没有做压缩;如果 Stalled 和 Queueing 明显,可能是同一时间并行请求太多,需要合并请求或减少资源数;如果 DNS Lookup 和 Initial connection 偏高,就要上预连接和长连接。

页面性能不是单一指标,而是要能定位“时间去哪了”。前端性能优化的本质,就是减少关键路径上的网络往返次数,并让每个往返传输的数据更小。

7.2 CDN、HTTP/2、资源合并的取舍

CDN 和 HTTP/2 已经是现代前端的标配。CDN 把资源分发到离用户更近的节点,减少网络往返的物理距离;HTTP/2 多路复用让多个资源可以共享一个连接。但两者结合后,资源合并策略要重新思考。

HTTP/1.1 时代流行把多个小文件合并成一个文件,减少请求次数。HTTP/2 时代反过来,过多合并会把本可以并行加载的内容合成一个串行下载的巨型文件,而且缓存失效粒度变大——改一行代码,整个大文件缓存失效。更合理的策略是:保持多个小资源文件,配置好缓存,让它们通过多路复用并行传输,然后配合文件名哈希做长期缓存。

这里有一个容易忽略的点:HTTP/2 的多路复用虽然让请求不再排队,但如果某个请求传输的数据量非常大,比如一个 20MB 的视频文件,它在连接上占用的带宽资源还是会影响同连接的其他资源。所以大文件要么走独立域名,要么走 CDN 的视频流协议,不能跟页面核心资源混在同一个连接里抢带宽。

7.3 前端视角的 TLS 优化与连接复用

TLS 握手是 HTTPS 下绕不开的成本。减少 TLS 握手次数,本质上就是减少往返时间。常见手段是:

  1. TLS 1.3 和 0-RTT 恢复,如果服务端支持,移动端弱网下提升明显。
  2. 连接复用和 HTTP/2 Keep-Alive,让一次握手服务多个请求。
  3. 对关键接口域名做preconnect,提前建立连接。

有些前端的误区是觉得 HTTPS 一定比 HTTP 慢。实际部署生产环境时,HTTPS 开了会话复用后,性能差距小到可以忽略。反而因为 HTTP/2 只支持 HTTPS,很多网站升级到 HTTPS 后顺便开了 HTTP/2,整体性能比原来还更好。

7.4 性能优化检查清单

分享一个我项目里常用的网络优化自检清单,不保证覆盖所有场景,但所有前端都值得过一遍:

  • HTML 是否设置了正确的缓存策略?有没有被强缓存卡住?
  • JS/CSS 文件是否都带了内容哈希?静态资源是否开启了immutable缓存?
  • 关键请求域名是否开启了 HTTP/2?连接复用是否生效?
  • 图片是否经过压缩?是否用懒加载?
  • 第三方脚本是否拖慢了首次加载?是否考虑异步加载或放到空闲时机?
  • 有没有大量小请求可以合并?有没有接口需要做防抖和节流?
  • 弱网时有没有降级方案?接口请求有没有超时和重试机制?
  • CDN 是否配置了合理的缓存时间?资源更新时能否及时失效?

8. 常见网络问题排查与实战速查表

8.1 状态码速查与前端处理建议

前端看状态码,不能只看是不是 200。整理了实际开发中高频遇到的几个状态码和处理思路,可以直接参照:

状态码含义前端处理建议
200OK正常响应
204No Content请求成功但无响应体,适合删除等操作
301Moved Permanently永久重定向,应更新 URL 引用
302Found / 307临时重定向,通常用于登录跳转
304Not Modified协商缓存生效,资源未修改
400Bad Request请求参数或格式不对,检查报文结构
401Unauthorized未认证,检查登录态和 Token
403Forbidden已认证但无权限,检查权限配置
404Not Found路径错误或资源下线
405Method Not Allowed请求方法不被允许
429Too Many Requests触发限流,需要考虑退避重试
500Internal Server Error服务端异常,抓服务端日志
502Bad Gateway网关或负载均衡没拿到有效响应
503Service Unavailable服务不可用,可能正在重启或过载
504Gateway Timeout网关超时,上游服务响应过慢

碰到 502、503、504 时,先把锅分清楚:是前端请求异常,还是网关问题,还是后端服务问题。在网上拿这个状态码去对答案,不如看一下网关日志和后端实例状态。

8.2 网络问题的分层排查思路

我总结了一个由下往上的排查顺序,遇到问题先不要慌,按这个顺序走:

  1. 先看物理层与网络联通性:本机能上网吗?目标域名能 ping 通吗?这个命令验证的是链路层到网络层的连通性。
  2. 再看 DNS:用nslookup或dig查域名解析结果是否正常,有没有解析到旧的 IP。
  3. 然后看 TCP 连接:用telnet IP 端口或nc测试目标端口的连通性;如果连不上,就查防火墙、安全组。
  4. 接着看 TLS/SSL:用openssl s_client查看证书链、证书有效期、TLS 版本。
  5. 最后看 HTTP 层:用curl -v看请求头、响应头、状态码、重定向过程,确认是不是业务层面的问题。

这套流程对前端也很有用。很多人一报错就怀疑是自己代码的问题,但先做好底层验证,能省掉大量无意义的排错时间。我曾经遇到过线上接口一会儿通一会儿不通,折腾了一下午,最后发现是 DNS 解析到的旧节点在做维护,换个 DNS 服务商就好了。

8.3 浏览器开发者工具 Network 面板实战

浏览器开发者工具的 Network 面板,是前端排查网络问题的主战场。除了看状态码和响应时间,我会重点看这几个细节:

  • 查看请求列表时,先按耗时排序,找到最慢的几个请求。
  • 用“Name”列旁边的类型过滤,只看 XHR/Fetch,排除静态资源干扰。
  • 在请求详情里看“Timing”页签,对照前面讲的各阶段时间。
  • 看响应头里的Cache-Control、ETag、Last-Modified,确认缓存是否生效。
  • 看请求头里的Origin、Referer、Cookie,排查跨域和鉴权问题。
  • 开启“Disable cache”模拟无缓存状态,能看出资源加载对缓存的依赖程度。

还有一个很实用的功能:右键请求 “Copy as fetch”或者“Copy as cURL”,可以复现请求做本地调试。临时要对某个接口做参数测试时,我用这个功能比对请求差异,比手写 fetch 快得多。

9. 学习路线与实操心得

9.1 前端计算机网络修炼路线

如果你想系统学,但是又没有大把时间啃厚重教材,我建议按下面的顺序来:

  1. 先看分层模型,建立整体框架。不用记全细节,重点搞懂每层的职责。
  2. 再抓一次真实请求。打开开发者工具,逐个字段看,对着请求头、响应头逐行查资料,用一天时间把一个请求从 DNS 到响应体的整个过程弄明白。
  3. 然后重点吃透 HTTP。状态码、缓存、跨域、HTTPS 这四个是面试和实战的高频点,每个都要动手验证。
  4. 接着看 TCP 和 UDP 的核心机制。三次握手、四次挥手、为什么 HTTP/2 要加 TLS、为什么实时通信不用 TCP。
  5. 再了解 HTTPS 的证书体系,并实际操作一次本地配置自签证书,理解证书链、加密套件、握手过程。
  6. 最后结合项目的性能优化和问题排查,把知识落回真实场景。

学这些内容不需要特别深的数学基础,但需要多抓包、多模拟、多读报文。不做实验只看书,记不住。

9.2 我个人踩坑后的真实体会

我见过太多人把“学了计算机网络”等同于“背下了八股文”,一到实战就露馅。我自己早期也有过一段尴尬期:知道三次握手的序号,但完全不知道线上大量TIME_WAIT意味着什么;能背出协缓存和强缓存的区别,却把Cache-Control写错了方向,导致用户永远拿到旧版本页面。

所以我很强调动手验证。自己开一个本地服务,用不同的Cache-Control头刷新页面看看缓存变化;自己在后端加一个 CORS 响应头,看看浏览器预检请求的完整链路;自己用弱网模式模拟丢包,感受 TCP 重传对应用层的可见影响。这些实验做一遍,比背十篇面经管用。

计算机网络对前端的价值,不在于让你能跟网络工程师争论路由协议,而在于你能准确感知线上每一毫秒、每一次请求背后发生了什么。当你看到页面加载慢,脑子里浮现的不是“好慢啊”,而是“慢在哪一层、哪个环节、哪个指标”,你就真正入门了。这套思维,是前端从“会写页面”走向“能解决复杂问题”的关键一步。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进