网络
这块「手写」不了,但它是八股占比最大的一块,也是线上事故最集中的一块。 学法不同:打开 DevTools 的 Network / Application 面板,用真实站点验证每一条。 每个知识点后面都…
这块「手写」不了,但它是八股占比最大的一块,也是线上事故最集中的一块。 学法不同:打开 DevTools 的 Network / Application 面板,用真实站点验证每一条。 每个知识点后面都有 🔬 实验步骤,照做一遍比背十遍管用。
10.1 HTTP 缓存 ⭐⭐(最高频,必须能画出决策流程图)
强缓存(不发请求)
| 头 | 版本 | 说明 |
|---|---|---|
Expires | HTTP/1.0 | 绝对时间,依赖客户端时钟,已基本废弃 |
Cache-Control | HTTP/1.1 | 相对时间,优先级更高 |
Cache-Control 常用值:
max-age=31536000缓存一年no-cache—— 不是不缓存!是「可以存,但每次用之前必须去服务器验证」(走协商缓存)no-store—— 这才是真正的不缓存public/private—— 能否被 CDN 等中间代理缓存immutable—— 告诉浏览器「这个文件内容永不改变,刷新也别来问」stale-while-revalidate=60—— 先用过期的,后台悄悄更新
协商缓存(发请求,可能返回 304)
| 请求头 | 响应头 | 精度 |
|---|---|---|
If-Modified-Since | Last-Modified | 秒级,且文件内容没变但 mtime 变了会误判 |
If-None-Match | ETag | 内容哈希,精确,但服务端有计算开销 |
优先级:Cache-Control > Expires;ETag > Last-Modified。
🔬 实验
- 打开任意站点 → Network → 刷新,看 Size 列:
(memory cache)/(disk cache)/304/ 真实大小 —— 分别对应什么? - 对比三种刷新方式:
- 普通刷新(F5):强缓存失效,走协商缓存
- 硬性刷新(Ctrl+Shift+R):带
Cache-Control: no-cache,全部重新拉 - 地址栏回车 / 前进后退:强缓存生效
- 勾上 Network 的
Disable cache,观察请求头多了什么。
🤔 思考题
- 前端打包为什么要给文件名加 hash(
app.a1b2c3.js)?它和Cache-Control: max-age=31536000, immutable是怎么配合的? index.html应该用什么缓存策略?(答案:no-cache,因为它是入口,必须能立刻更新)- 用户反馈「发版后页面还是旧的」,你怎么排查?(HTML 缓存 / CDN 缓存 / Service Worker / 浏览器强缓存)
10.2 跨域 ⭐⭐
同源策略
协议 + 域名 + 端口,三者全同才叫同源。限制的是:Ajax 请求、Cookie/LocalStorage 读取、DOM 访问。
不限制:<img> <script> <link> <iframe> 的加载(这正是 JSONP 和 CSRF 的基础)。
CORS(标准方案)
简单请求 vs 预检请求(preflight) 的分界线:
简单请求需同时满足:
- 方法是
GET/POST/HEAD - 头只含安全字段(
Accept、Content-Type、Content-Language…) Content-Type只能是text/plain/multipart/form-data/application/x-www-form-urlencoded
→ 所以你发 Content-Type: application/json 的 POST,一定会先发一个 OPTIONS 预检。
关键响应头:
Access-Control-Allow-Origin: https://a.com // 不能用 * 如果要带 cookie
Access-Control-Allow-Credentials: true // 配合前端 credentials: 'include'
Access-Control-Allow-Methods: GET,POST,PUT
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Max-Age: 86400 // 预检结果缓存多久,能大幅减少 OPTIONS
Access-Control-Expose-Headers: X-Total-Count // 前端想读非标准响应头必须在这里声明其它方案
- 开发代理(Vite
server.proxy/ webpack devServer)—— 最常用,本质是浏览器同源,node 服务端转发(服务端之间没有同源限制) - Nginx 反向代理 —— 生产环境同理
- JSONP —— 只支持 GET,靠
<script>不受同源限制;已淘汰但仍是面试题(✍️ 去手写一遍,20 行) - postMessage —— iframe / 窗口间通信
- document.domain —— 已废弃
🔬 实验
在任意站点的 Console 里 fetch('https://api.github.com/users/octocat'),观察成功;
再 fetch('https://www.baidu.com'),看 CORS 报错信息长什么样,学会读它。
🤔 思考题
- CORS 报错时,请求到底有没有发出去?(发了!服务器也处理了,是浏览器拦下了响应。所以 CORS 不是安全机制,防不了服务端被调用)
- 为什么
Access-Control-Allow-Origin: *时不能带 Cookie? - 前端能不能通过修改代码绕过 CORS?(不能,这是浏览器行为。但可以用代理)
10.3 HTTP 版本演进 ⭐
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| 传输层 | TCP | TCP | QUIC(基于 UDP) |
| 并发 | 每域名 6 个连接,队头阻塞 | 单连接多路复用 | 多路复用,且解决了 TCP 层队头阻塞 |
| 头部 | 纯文本,每次重复发 | HPACK 压缩 | QPACK |
| 推送 | 无 | Server Push(已被主流废弃) | 无 |
| 格式 | 文本 | 二进制分帧 | 二进制 |
对前端的实际影响:HTTP/2 之后,「合并雪碧图、合并 JS 减少请求数」这些老优化手段大部分失效甚至有害了(一个大包改一行就全失效,不如多个小包利于缓存)。
🤔 思考题
- HTTP/2 解决了应用层的队头阻塞,为什么 TCP 层的还在?HTTP/3 怎么解决的?
- 你的项目用的是哪个版本?(Network 面板右键表头 → 勾 Protocol 列,看
h2/h3)
10.4 存储 ⭐
| 容量 | 生命周期 | 随请求发送 | 场景 | |
|---|---|---|---|---|
| Cookie | 4KB | 可设 Expires | ✅ 每次都发 | 会话标识 |
| localStorage | 5-10MB | 永久 | ❌ | 用户偏好、非敏感缓存 |
| sessionStorage | 5-10MB | 标签页关闭即失效 | ❌ | 单次流程的临时数据 |
| IndexedDB | 几百 MB+ | 永久 | ❌ | 离线数据、大文件 |
| Cache Storage | 大 | 永久 | ❌ | Service Worker 离线缓存 |
Cookie 的安全属性(必背):
HttpOnly—— JS 读不到,防 XSS 窃取Secure—— 只在 HTTPS 下发送SameSite=Strict/Lax/None—— 防 CSRF;现代浏览器默认LaxDomain/Path—— 作用范围
🤔 思考题
- 为什么 token 放 localStorage 有 XSS 风险,放 HttpOnly Cookie 又有 CSRF 风险?现在主流怎么权衡?
- 一个域名下的 localStorage,不同端口能互通吗?(能 —— localStorage 只按「协议+域名+端口」隔离,注意这点和同源策略一致)
10.5 Web 安全 ⭐⭐
XSS(跨站脚本)
| 类型 | 原理 |
|---|---|
| 存储型 | 恶意脚本存进数据库,所有访问者中招(评论区最典型) |
| 反射型 | 恶意脚本在 URL 里,服务端原样吐回页面 |
| DOM 型 | 纯前端造成,比如 location.hash 直接塞进 innerHTML |
防御:
- 输出编码(最核心)—— 把
< > " ' &转义 - 不用
innerHTML/document.write/eval;React 的dangerouslySetInnerHTML要过 DOMPurify - CSP(Content-Security-Policy 响应头)—— 白名单限制脚本来源,是最后一道防线
- Cookie 加
HttpOnly - 输入校验(辅助,不是主要手段 —— 因为攻击面在输出)
🤔 React/Vue 默认为什么「几乎不会 XSS」?(模板里的插值默认自动转义。但
dangerouslySetInnerHTML、v-html、href="javascript:..."、动态<script src>仍然是口子)
CSRF(跨站请求伪造)
原理:利用你在 A 站的登录态,诱导你在 B 站发一个指向 A 站的请求(浏览器会自动带 Cookie)。
防御:
SameSite=Lax/StrictCookie(现代浏览器默认,已挡掉大部分)- CSRF Token —— 服务端下发随机 token,前端每次请求带上(攻击者拿不到)
- 校验
Origin/Referer - 关键操作二次验证
其它必知
- 点击劫持 Clickjacking:
X-Frame-Options: DENY或 CSP 的frame-ancestors - 中间人攻击:HTTPS + HSTS(
Strict-Transport-Security) - 依赖投毒 / 供应链:
npm audit、锁文件、SRI(<script integrity="sha384-...">) - 原型链污染:
JSON.parse的数据直接 merge 进对象,攻击者塞__proto__键 → 污染所有对象。lodash 早年出过这个 CVE
🔬 实验
- 在本地起个页面,把
location.hash直接innerHTML进去,然后访问#<img src=x onerror=alert(1)>—— 亲手打一次 DOM XSS。 - 给页面加
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">,看内联脚本被拦截的报错。
10.6 从输入 URL 到页面显示 ⭐⭐(终极八股,串起所有知识)
答这题的关键不是背步骤,而是能在每一步展开细节:
- URL 解析 → 判断是搜索词还是 URL,补全协议
- DNS 解析 → 浏览器缓存 → 系统 hosts → 本地 DNS → 根/顶级/权威域名服务器(递归+迭代查询)
- 优化:
dns-prefetch、减少域名数
- 优化:
- TCP 三次握手 → 为什么是三次不是两次?(防止历史连接的重复请求浪费资源)
- 优化:
preconnect、Keep-Alive
- 优化:
- TLS 握手(HTTPS)→ 协商密钥(非对称加密交换 → 对称加密传输)、验证证书链
- 优化:TLS 1.3 的 1-RTT / 0-RTT、会话复用
- 发送 HTTP 请求 → 走缓存判断(10.1 那套流程)
- 服务端处理 & 响应 → 可能有重定向、CDN 命中
- 浏览器解析渲染 → 见模块 06 的关键渲染路径
- HTML 解析成 DOM(遇到
<script>阻塞,defer/async的差别) - CSS 解析成 CSSOM(阻塞渲染,不阻塞解析)
- Render Tree → Layout → Paint → Composite
- HTML 解析成 DOM(遇到
- JS 执行 → 事件循环接管(模块 01)
- TCP 四次挥手 → 为什么比握手多一次?
🤔 每一步都能追问
- 为什么 DNS 用 UDP?
- HTTPS 具体加密了什么?证书怎么验证的?中间人为什么伪造不了?
- 首屏优化能在上面哪几步下手?(几乎每一步都能)
10.7 性能指标 · Core Web Vitals ⭐
| 指标 | 全称 | 好的标准 | 衡量什么 |
|---|---|---|---|
| LCP | Largest Contentful Paint | < 2.5s | 加载速度(最大内容元素绘制完) |
| INP | Interaction to Next Paint | < 200ms | 交互响应(2024 年取代了 FID) |
| CLS | Cumulative Layout Shift | < 0.1 | 视觉稳定性(内容乱跳) |
其它常用:FCP(首次内容绘制)、TTFB(首字节)、TTI(可交互)、TBT(总阻塞时间)。
怎么测:Lighthouse(实验室数据)、PerformanceObserver API(真实用户数据 RUM)、Chrome DevTools 的 Performance Insights。
🤔 思考题
- CLS 变差的常见原因?(图片没设宽高、字体切换 FOUT、动态插入广告/横幅 → 解法:
aspect-ratio、font-display: optional、给占位) - INP 差说明什么?(主线程长任务太多 → 回到模块 01 的时间切片,和 React 的并发特性)
结业检查
- 能默画一张「浏览器发起请求时的缓存决策流程图」
- 能解释一次 CORS 预检的完整过程,并说清 CORS 不是安全机制
- 能说出 XSS 和 CSRF 各三种防御手段,并说明为什么输出编码比输入过滤重要
- 能对着 Network 面板讲清楚一个真实站点的加载瀑布图
- 「从输入 URL 到页面显示」能讲 10 分钟不重复
反向链接(0)
还没有笔记链接到这里。在 Obsidian 里用 [[笔记名]] 引用它,这里就会出现,关系图谱上也会多出一条连线。