学完本章,你能完整回答前端面试的经典问题「从输入 URL 到页面显示发生了什么」,能独立配置缓存策略,能说清跨域和安全的底层原理。这是从「会用浏览器」到「懂浏览器」的转折点。
现代浏览器为什么快、为什么一个页面崩溃不影响其他页面——答案是进程隔离。
Chrome 是多进程浏览器,主要进程:
| 进程 | 职责 | 关键点 |
|---|---|---|
| 浏览器主进程 | 界面、标签页管理、网络请求 | 负责与操作系统交互 |
| 渲染进程(每标签页) | HTML/CSS/JS 解析与渲染 | 包含主线程、合成线程等;沙箱隔离 |
| GPU 进程 | 图形处理 | 合成加速、3D 渲染 |
| 网络进程 | 网络资源加载 | 统一管理请求 |
| 插件进程 | 运行插件 | 隔离,防止插件拖垮浏览器 |
这也是阶段二事件循环的「物理载体」:主线程跑同步代码和微任务,宏任务从事件队列取。
这条主线串起网络、缓存、渲染、JS 执行的全过程。
<!-- 普通 script:会阻塞 HTML 解析,先下载并执行完才继续 -->
<script src="a.js"></script>
<!-- defer:并行下载,但等 HTML 解析完才执行(按顺序)-->
<script defer src="a.js"></script>
<!-- async:并行下载,下载完立即执行(不保证顺序)-->
<script async src="a.js"></script>
// 推荐:普通脚本放 body 底部,或使用 defer
前端开发每天都和 HTTP 打交道,这些基础必须烂熟。
// 请求(请求行 + 请求头 + 请求体)
POST /api/login HTTP/1.1 // 方法 路径 版本
Host: example.com
Content-Type: application/json
Authorization: Bearer <token>
{"username":"zhangsan","password":"***"}
// 响应(状态行 + 响应头 + 响应体)
HTTP/1.1 200 OK // 版本 状态码 原因短语
Content-Type: application/json
Cache-Control: max-age=3600
{"token":"eyJ..."}
| 范围 | 代表 | 含义 |
|---|---|---|
| 2xx 成功 | 200 / 201 / 204 | 成功 / 创建成功 / 无内容 |
| 3xx 重定向 | 301 / 302 / 304 | 永久重定向 / 临时重定向 / 缓存未修改 |
| 4xx 客户端错误 | 400 / 401 / 403 / 404 / 429 | 参数错 / 未登录 / 无权限 / 不存在 / 请求过频 |
| 5xx 服务端错误 | 500 / 502 / 503 / 504 | 服务器错 / 网关错 / 服务不可用 / 超时 |
| 方法 | 语义 | 特点 |
|---|---|---|
| GET | 获取资源 | 参数在 URL,有长度限制,会被缓存/历史记录 |
| POST | 提交数据 | 参数在请求体,无长度限制,不缓存 |
| PUT / PATCH | 整体替换 / 部分更新 | RESTful 风格 |
| DELETE | 删除资源 | RESTful 风格 |
| OPTIONS | 预检请求 | CORS 跨域时自动发出 |
| HEAD | 只要响应头 | 检测资源是否存在 |
缓存是性能优化性价比最高的一环,也是面试高频考点。
// 服务器在响应头里返回,浏览器下次直接用缓存,不发请求
Cache-Control: max-age=3600 // 1 小时内直接用缓存
Cache-Control: no-cache // 每次都要重新验证(协商)
Cache-Control: no-store // 完全不缓存
Expires: Wed, 21 Oct 2026 07:28:00 GMT // 老方案,HTTP/1.0,已被 Cache-Control 取代
max-age 优先级高于 Expires。// 第一次请求:服务器返回资源 + 标识
ETag: "abc123" // 内容的指纹(优先级高)
Last-Modified: Wed, 19 Aug 2026 08:00:00 GMT // 最后修改时间
// 第二次请求:浏览器带上标识问服务器
If-None-Match: "abc123" // 对应 ETag
If-Modified-Since: Wed, 19 Aug 2026 08:00:00 GMT // 对应 Last-Modified
// 服务器判断:没变 → 返回 304(无响应体),浏览器用缓存
// 变了 → 返回 200 + 新资源
max-age=31536000),文件变了哈希变,URL 变自动拉新。HTML:no-cache 每次协商,保证拿到最新入口。这套策略是「版本更新」的最佳实践。
协议演进史就是性能与安全的历史。
HTTP/3 更进一步:基于 UDP(QUIC),解决了 TCP 层的队头阻塞,连接建立更快(0-RTT),弱网表现更好。
存数据选哪个?容量、生命周期、场景是关键。
| 方案 | 容量 | 生命周期 | 特点 | 典型场景 |
|---|---|---|---|---|
| Cookie | 约 4KB | 可设置过期;关浏览器不一定消失 | 每次请求自动携带;可被 JS 读(可设 HttpOnly 防读) | 会话标识(登录态) |
| localStorage | 约 5MB | 永久,需手动清 | 同步 API;只能存字符串 | 主题、偏好、长期缓存 |
| sessionStorage | 约 5MB | 标签页关闭即清 | 同步 API;标签页隔离 | 表单草稿、页内临时数据 |
| IndexedDB | 大(数百 MB+) | 永久 | 异步 API;对象存储;支持索引与事务 | 离线数据、大文件、复杂结构化数据 |
// localStorage 使用(只能存字符串,对象要 JSON 序列化)
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme"); // "dark"
localStorage.removeItem("theme");
// 存对象:JSON.stringify / JSON.parse 包裹
跨域是前端开发每天都要处理的现实问题,原理必须清楚。
「源」= 协议 + 域名 + 端口,三者完全相同才算同源。浏览器出于安全,默认禁止跨源读取资源。判断例子:
// 页面在 https://a.com:443/index.html
https://a.com/api // ✅ 同源
https://a.com:8080/api // ❌ 端口不同
http://a.com/api // ❌ 协议不同
https://b.com/api // ❌ 域名不同
核心思路:服务器在响应头里「批准」某个源可以访问。浏览器看到许可就放行。
// 服务器返回的响应头(关键就这一行)
Access-Control-Allow-Origin: https://a.com // 允许哪个源;* 表示任意源
// 复杂请求(自定义头、非简单方法)会先发 OPTIONS 预检
// 服务器回应:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true // 允许携带 Cookie(此时 Origin 不能是 *)
安全不是安全团队的专属话题,前端的每一行代码都可能是入口。
攻击者把恶意脚本注入页面,脚本在受害者浏览器里执行,可偷 Cookie、伪造操作、窃取数据。
// 反射型/存储型:把用户输入当 HTML 输出
// ❌ 危险:用户在评论区输入 <script>偷Cookie</script>,被执行
comment.innerHTML = userInput;
// ✅ 安全:用 textContent,内容只当纯文本
comment.textContent = userInput;
| 类型 | 原理 | 例子 |
|---|---|---|
| 反射型 | 恶意脚本藏在 URL 参数里,一次性 | ?search=<script>... |
| 存储型 | 恶意脚本存到服务器,所有访问者中招 | 评论区、昵称 |
| DOM 型 | 脚本在客户端由 DOM 操作注入 | innerHTML 拼接 |
Content-Security-Policy,只允许指定来源的脚本执行。攻击者诱导你点开一个恶意页面,该页面偷偷向你的目标网站发请求——浏览器自动带上你的 Cookie,服务器以为是你在操作。
// 攻击页面里藏着一个自动发出的请求:
<img src="https://bank.com/api/transfer?to=黑客&amount=10000">
// 你的 Cookie 会自动带上,服务器以为是你的操作!
SameSite=Lax/Strict,跨站请求不携带(现代浏览器默认 Lax,效果最好)。本章以理解为主,但也要动手验证。
打开自己阶段一的个人主页,用 DevTools 的 Network 面板:① 逐个资源看加载时间、缓存命中情况;② 用 Performance 面板录制加载过程,找到关键渲染路径上的阻塞资源;③ 用 Lighthouse 打分并记录优化前后对比。把分析写成一份报告。
用流程图软件或手绘,画出完整链路(DNS → 连接 → 请求缓存 → 响应 → 解析 → 渲染树 → 布局 → 绘制 → 合成),每步标注「谁能优化、怎么优化」。这份笔记未来面试前复习,价值极高。
① 本地起两个端口互相请求,观察跨域报错;② 用 vite 的 proxy 配置解决开发期跨域;③ 用 Node 写一个带 CORS 头的接口,验证预检请求 OPTIONS 的完整流程。
本章核心是「能讲清楚」,每题都试着讲给别人听。
答案:① 输入 URL(判断是网址还是搜索词)→ ② DNS 解析(域名翻译成 IP,先查本地缓存)→ ③ 建立连接(TCP 三次握手 + HTTPS 的 TLS 握手)→ ④ 发送请求(先查本地缓存,命中则直接使用)→ ⑤ 服务器响应(返回 HTML 及状态码)→ ⑥ 解析 HTML(构建 DOM 树 + CSSOM 树)→ ⑦ 构建渲染树(DOM + CSSOM 合并,剔除不可见节点)→ ⑧ 布局 → 绘制 → 合成(计算位置、画像素、分层合成最终画面)。
答案:① 稳定性——一个标签页崩溃不影响其他标签页(进程有独立内存空间);② 安全性——渲染进程在沙箱中运行,网页代码不能直接访问操作系统;③ 性能——多核 CPU 可并行执行不同进程。代价是内存占用高(浏览器的经典取舍)。
答案:两者都让 script 并行下载不阻塞 HTML 解析。区别在执行时机:defer 等 HTML 全部解析完才执行,多个 defer 按顺序执行(适合有依赖的脚本,放在 head 中);async 下载完立刻执行,多个 async 不保证顺序(适合独立脚本如广告、统计代码)。普通 script 会阻塞解析,先下载并执行完才继续。
答案:回流(Reflow):元素尺寸、位置改变,浏览器须重新计算布局(最耗性能)。触发:改 width/height/margin/padding、增删 DOM、窗口缩放、读 offsetHeight/offsetTop 等布局属性。重绘(Repaint):样式改变但布局不变,只重新绘制像素(较便宜)。触发:改 color/background/border-color 等。合成:transform / opacity 变化只触发合成(最便宜,由合成线程在 GPU 处理,不占主线程)。
答案:200 成功(资源正常返回);301 永久重定向(资源永久迁移到新 URL,浏览器会缓存);304 协商缓存命中(资源未修改,用本地缓存);401 未认证(未登录,需身份验证);403 禁止访问(已登录但无权限);404 资源不存在(URL 写错或资源已删除);500 服务器内部错误(后端代码异常);502 网关错误(上游服务器无响应或返回无效,常见于 Nginx → 后端链路)。
答案:强缓存——浏览器直接使用本地缓存,不发任何请求。响应头:Cache-Control: max-age=3600(HTTP/1.1,优先级高)或 Expires: <绝对时间>(HTTP/1.1 之前)。命中时状态码 200(from memory/disk cache)。协商缓存——每次请求都发向服务器,由服务器判断资源是否更新。响应头:ETag(内容指纹,优先级高)+ 请求头 If-None-Match;或 Last-Modified(修改时间)+ 请求头 If-Modified-Since。未更新返回 304,更新了返回 200。
Cache-Control 关键值:max-age=N(强缓存 N 秒)、no-cache(强缓存失效,每次都协商)、no-store(完全不缓存)。ETag 比 Last-Modified 更精准(后者只能精确到秒,且改了内容再改回来会误判)。两者同时存在时服务器优先用 ETag 判断。答案:静态资源(JS/CSS/图片)使用「文件名带内容哈希 + 长期强缓存」策略——如 app.a1b2c3.js 配置 Cache-Control: max-age=31536000(一年)。文件名用哈希是因为文件内容一变哈希就变,URL 随之改变,浏览器视为全新资源自动拉取最新版本,彻底解决缓存一致性问题。HTML 则配 no-cache,每次都走协商缓存,确保永远拿到最新的入口文件。
答案:Cookie — 约 4KB;可设置过期时间,关浏览器不自动消失;每次请求自动携带;会话标识(登录态),可设 HttpOnly/Secure/SameSite。localStorage — 约 5MB;永久,需手动清除;同步 API,只能存字符串;主题、偏好、长期缓存。sessionStorage — 约 5MB;标签页关闭即清除;同步 API,标签页隔离;表单草稿、页内临时数据。IndexedDB — 数百 MB+;永久;异步 API,支持索引和事务;离线数据、大文件、复杂结构化数据。
答案:「源」= 协议 + 域名 + 端口,三者完全相同才算同源。浏览器出于安全默认禁止跨源读取资源。跨域报错是浏览器的问题——请求其实发出去了,服务器也正常处理并返回了,但浏览器发现响应头没有许可,就拦截了 JS 读取响应。CORS 解法:服务器在响应头里返回 Access-Control-Allow-Origin: https://a.com(允许的源),浏览器看到许可就放行了。需要带 Cookie 时加 Access-Control-Allow-Credentials: true,此时 Origin 不能写 *,必须指定具体源。
答案:预检请求(Preflight)是浏览器在发送「复杂请求」之前自动发出的一个 OPTIONS 请求,用于询问服务器「这个跨域请求是否被允许」。服务器需在响应中返回:Access-Control-Allow-Methods(允许方法)、Access-Control-Allow-Headers(允许请求头)、Access-Control-Allow-Credentials(允许携带 Cookie)。只有服务器通过了预检,浏览器才会真正发出实际请求。
application/x-www-form-urlencoded、multipart/form-data、text/plain(常见:application/json 会触发);③ 请求中包含自定义头(如 Authorization: Bearer token)。注意:每次跨域复杂请求都会预检,可通过 Access-Control-Max-Age 设置预检结果缓存时间(秒)来减少预检频率。答案:反射型——恶意脚本藏在 URL 参数中,服务器未过滤直接把参数拼进 HTML 返回,用户点开链接即中招(一次性,需诱导点击)。存储型——恶意脚本被保存到服务器(评论区、昵称、私信等),所有访问该页面的用户都会中招(危害最大)。DOM 型——恶意脚本由客户端 JS 的 DOM 操作直接注入(如使用 innerHTML 拼接用户输入),不经过服务器。防范要点:前两类是服务器输出时未转义,第三类是前端 JS 未做文本化处理。
Content-Security-Policy 响应头,限制只能执行指定来源的脚本;③ Cookie 加 HttpOnly——JS 通过 document.cookie 读不到该 Cookie,被 XSS 偷了也没用。一句话总结:永远不要信任用户输入,输出前必须转义。答案:攻击原理:用户登录了银行网站(Cookie 有效),然后访问了攻击者构造的恶意页面。该页面自动向银行发起带副作用的请求(如 <img src="https://bank.com/transfer?to=hacker&amount=10000">),浏览器自动携带用户的 Cookie,银行误以为是用户本人的操作。SameSite Cookie 防御原理:Cookie 设 SameSite=Lax(默认值)时,第三方站点发起的请求(如 img/iframe/submission)不会带上该 Cookie。于是银行收到的转账请求没有登录 Cookie,认为未认证从而拒绝。SameSite=Strict 更强,连从第三方站点点击链接跳转时都不带 Cookie。
答案:加密原理——HTTPS = HTTP + TLS(传输层安全协议),采用混合加密:① 非对称加密(RSA / ECC)在握手阶段安全交换一个「会话密钥」;② 之后使用对称加密(AES)高效传输实际数据。非对称加密只用来解决密钥分发问题(计算慢但安全),对称加密负责数据传输(速度快)。为什么需要证书——证明「这个网站是真的,不是中间人伪装的」。证书由受信任的 CA(数字证书认证机构)签发,浏览器内置信任根证书。没有证书,中间人可以伪造服务器拿到会话密钥,混合加密就形同虚设。
答案:HTTP/2 四大改进:① 多路复用——一个 TCP 连接并发多个请求/响应流,解决 HTTP/1.1 的队头阻塞(一个请求卡住后面全卡),无需开多个 TCP 连接(告别域名分片等 hack);② 头部压缩(HPACK)——重复的请求头只传增量,大幅减少 header 体积(HTTP/1.1 每次请求均带完整 header);③ 二进制分帧——数据拆为帧传输,比 HTTP/1.1 的文本协议更高效解析;④ 服务端推送——服务器可主动推送客户端需要的资源(实际采用率不高)。HTTP/3 进一步基于 QUIC(UDP)解决 TCP 层的队头阻塞,连接建立更快(0-RTT)。