前端网络知识指南
一、HTTP 核心知识
1.1 HTTP 报文结构
1.1.1 请求报文结构
1.1.2 响应报文结构
1.2 HTTP 方法详解
fetch('/api/users?id=1', {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Alice', age: 25 })
});
fetch('/api/users/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Alice', age: 26 })
});
fetch('/api/users/1', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ age: 26 })
});
fetch('/api/users/1', {
method: 'DELETE'
});
fetch('/api/users/1', {
method: 'HEAD'
});
fetch('/api/users', {
method: 'OPTIONS',
headers: {
'Access-Control-Request-Method': 'POST',
'Access-Control-Request-Headers': 'Content-Type'
}
});
1.3 HTTP 持久连接与管线化
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Hello' }));
console.log('Connection:', req.headers.connection);
});
server.listen(3000);
const connectionComparison = {
'HTTP/1.0': {
'持久连接': '不支持,每次请求都建立新连接',
'管线化': '不支持',
'并发': '需要多个连接(浏览器通常限制 6-8 个)'
},
'HTTP/1.1': {
'持久连接': '支持,默认开启',
'管线化': '支持,但不常用(队头阻塞)',
'并发': '需要多个连接'
},
'HTTP/2': {
'持久连接': '支持,只能有一个连接',
'管线化': '不支持(用多路复用替代)',
'并发': '多路复用,在一个连接中并发传输'
},
'HTTP/3': {
'持久连接': '基于 UDP,不存在连接概念',
'管线化': '支持(QUIC 层面解决队头阻塞)',
'并发': '真正的并发,无队头阻塞'
}
};
二、HTTPS 与 TLS
2.1 HTTPS 加密原理
const crypto = require('crypto');
function symmetricEncrypt(data, key) {
const iv = Buffer.alloc(16, 0);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return { iv: iv.toString('hex'), encrypted };
}
function symmetricDecrypt(encrypted, key, iv) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
function generateKeyPair() {
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
return { publicKey, privateKey };
}
function rsaEncrypt(data, publicKey) {
const buffer = Buffer.from(data);
const encrypted = crypto.publicEncrypt(
{ key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
buffer
);
return encrypted.toString('base64');
}
function rsaDecrypt(encrypted, privateKey) {
const buffer = Buffer.from(encrypted, 'base64');
const decrypted = crypto.privateDecrypt(
{ key: privateKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
buffer
);
return decrypted.toString('utf8');
}
const https = require('https');
const fs = require('fs');
const options = {
cert: fs.readFileSync('/path/to/cert.pem'),
key: fs.readFileSync('/path/to/key.pem'),
ca: fs.readFileSync('/path/to/ca.pem'),
requestCert: true,
rejectUnauthorized: false
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('HTTPS secured');
});
server.listen(443);
const tlsVersions = {
'SSL 2.0': { year: 1995, status: '废弃', issues: '已知安全漏洞' },
'SSL 3.0': { year: 1996, status: '废弃', issues: 'POODLE 攻击' },
'TLS 1.0': { year: 1999, status: '废弃', issues: 'BEAST 攻击' },
'TLS 1.1': { year: 2006, status: '废弃', issues: '已知安全漏洞' },
'TLS 1.2': { year: 2008, status: '推荐', features: 'AEAD、SHA-256、AES-GCM' },
'TLS 1.3': { year: 2018, status: '最新', features: '1-RTT、0-RTT、前向保密' }
};
2.2 数字证书与 CA
function certificatePinning() {
const pinnedCertificates = [
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=',
'sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB='
];
return function verifyCertificate(certificate) {
const certHash = calculateHash(certificate);
if (pinnedCertificates.includes(certHash)) {
return true;
}
return verifyCertificateChain(certificate, pinnedCertificates);
};
}
三、DNS 与域名解析
3.1 DNS 解析流程
const dnsRecordTypes = {
'A': {
'作用': '将域名映射到 IPv4 地址',
'示例': 'example.com -> 93.184.216.34',
'使用场景': '最常见的 DNS 记录'
},
'AAAA': {
'作用': '将域名映射到 IPv6 地址',
'示例': 'example.com -> 2606:2800:220:1::248:1893',
'使用场景': 'IPv6 环境'
},
'CNAME': {
'作用': '将域名别名映射到另一个域名',
'示例': 'www.example.com -> example.com',
'使用场景': 'CDN 加速、多域名同站点'
},
'MX': {
'作用': '指定邮件服务器地址',
'示例': 'example.com -> mail.example.com',
'优先级': '数值越小优先级越高'
},
'TXT': {
'作用': '存储文本信息',
'示例': '用于 SPF、DKIM 验证',
'使用场景': '邮件安全、反垃圾邮件'
},
'NS': {
'作用': '指定域名服务器',
'示例': 'example.com -> ns1.example.com',
'使用场景': '子域名授权'
},
'SOA': {
'作用': '权威记录起始',
'包含': '主服务器、邮箱、序列号、刷新时间等'
}
};
const dnsProtocol = {
'DNS over UDP': {
'端口': 53,
'优点': '速度快,开销小',
'限制': '单个包限制 512 字节',
'使用场景': '大多数 DNS 查询'
},
'DNS over TCP': {
'端口': 53,
'优点': '支持大包传输',
'使用场景': 'Zone Transfer、大数据包响应'
},
'DNS over HTTPS (DoH)': {
'端口': 443,
'优点': '加密、防劫持、绕过审查',
'使用场景': '隐私保护'
},
'DNS over TLS (DoT)': {
'端口': 853,
'优点': '加密、防劫持',
'使用场景': '隐私保护'
}
};
const dns = require('dns');
function resolveDNS() {
dns.resolve('example.com', (err, addresses) => {
if (err) console.error(err);
else console.log('IP:', addresses);
});
dns.resolve4('example.com', (err, addresses) => {
console.log('IPv4:', addresses);
});
dns.resolve6('example.com', (err, addresses) => {
console.log('IPv6:', addresses);
});
dns.reverse('93.184.216.34', (err, hostnames) => {
console.log('Hostnames:', hostnames);
});
const { promisify } = require('util');
const resolve4Promise = promisify(dns.resolve4);
resolve4Promise('example.com').then(addresses => {
console.log('IP:', addresses);
});
}
3.2 CDN 原理
const cdnScheduling = {
'DNS 智能解析': {
'原理': '根据用户 IP 返回最近节点的 IP',
'优点': '简单易实现',
'缺点': '依赖 DNS 服务器准确性'
},
'HTTP 重定向': {
'原理': '返回 302 指向最近节点',
'优点': '精确控制',
'缺点': '多一次 HTTP 请求'
},
'Anycast': {
'原理': '同一 IP 映射到多个物理位置',
'优点': '路由层自动选最近',
'缺点': '需要网络层面支持'
}
};
const originConfig = {
'回源协议': 'HTTP 或 HTTPS',
'回源 HOST': '源站域名或 IP',
'回源路径': '/path 或 /',
'回源超时': {
'连接': '5 秒',
'读取': '30 秒'
},
'回源重试': '2 次'
};
四、TCP/IP 与网络协议
4.1 TCP 三次握手与四次挥手
const tcpHandshake = {
'CLOSED': '无连接',
'SYN_SENT': '已发送 SYN,等待 ACK(客户端)',
'SYN_RECEIVED': '已收到 SYN,发送 SYN+ACK(服务器)',
'ESTABLISHED': '连接建立完成,双方可以传输数据'
};
const tcpHandshakeParams = {
'SYN': {
'作用': '同步序列号',
'值': '1 表示这是一个连接请求'
},
'seq (序列号)': {
'作用': '标识发送的数据字节位置',
'初始化': '随机数(防止重放攻击)'
},
'ack (确认号)': {
'作用': '期望收到的下一个字节',
'计算': '已经收到的 seq + 已接收数据长度'
},
'ACK': {
'作用': '确认标志',
'值': '1 表示这是一个确认应答'
}
};
const tcpWave = {
'FIN_WAIT_1': '已发送 FIN,等待 ACK(客户端)',
'FIN_WAIT_2': '已收到 ACK,等待服务器 FIN',
'CLOSE_WAIT': '已收到 FIN,等待应用处理(服务器)',
'CLOSING': '双方同时发送 FIN,收到对方 ACK',
'LAST_ACK': '已收到对方 FIN,等待 ACK 确认',
'TIME_WAIT': '收到 FIN 并发送 ACK,等待 2MSL'
};
const timeWaitReasons = {
'MSL': 'Maximum Segment Lifetime,报文最大生存时间(约 60 秒)',
'原因1': '确保对方收到 ACK(如果 ACK 丢失,对方会重发 FIN)',
'原因2': '让本连接的所有报文在网络中消失(避免影响新连接)'
};
const tcpStateDiagram = `
CLOSED -> SYN_SENT (发送 SYN)
CLOSED -> LISTEN (被动打开)
LISTEN -> SYN_RECEIVED (收到 SYN)
SYN_SENT -> ESTABLISHED (收到 SYN+ACK)
SYN_RECEIVED -> ESTABLISHED (收到 ACK)
ESTABLISHED -> FIN_WAIT_1 (主动关闭)
ESTABLISHED -> CLOSE_WAIT (收到 FIN)
FIN_WAIT_1 -> FIN_WAIT_2 (收到 ACK)
FIN_WAIT_2 -> CLOSING (收到 FIN)
FIN_WAIT_1 -> TIME_WAIT (收到 FIN+ACK)
CLOSING -> TIME_WAIT (收到 ACK)
CLOSE_WAIT -> LAST_ACK (发送 FIN)
LAST_ACK -> CLOSED (收到 ACK)
TIME_WAIT -> CLOSED (2MSL 超时)
`;
const tcpReliable = {
'确认应答 (ACK)': '收到数据后发送 ACK 确认',
'超时重传': '发送后等待 ACK,超时则重发',
'序列号': '每个字节都有唯一序列号',
'流量控制': '滑动窗口避免发送过快',
'拥塞控制': '慢启动、拥塞避免、快速恢复'
};
function slidingWindow() {
const sendWindow = {
'已发送已确认': 0,
'已发送未确认': 1000,
'可用窗口': 2000,
'总窗口大小': 3000
};
}
const tcpCongestion = {
'慢启动 (Slow Start)': {
'开始': 'cwnd = 1 MSS',
'增长': '每收到一个 ACK,cwnd += 1 MSS',
'终止': '达到慢启动阈值 ssthresh 或出现丢包'
},
'拥塞避免': {
'开始': 'cwnd 达到 ssthresh',
'增长': '每收到一个 ACK,cwnd += 1/cwnd(线性增长)'
},
'快速重传': {
'触发': '收到 3 个重复 ACK',
'动作': '立即重发丢失的 segment,不等超时'
},
'快速恢复': {
'动作': 'ssthresh = cwnd/2, cwnd = ssthresh + 3, 进入拥塞避免'
}
};
4.2 HTTP 缓存机制
const cacheControlDirectives = {
'max-age=3600': {
'含义': '资源在 3600 秒内新鲜',
'计算': '缓存有效期 = 缓存创建时间 + max-age',
'示例': 'Cache-Control: max-age=3600'
},
'no-cache': {
'含义': '不使用强缓存,每次都发请求到服务器(可能用协商缓存)',
'注意': '不等于 no-store'
},
'no-store': {
'含义': '完全不缓存,所有请求都直接到服务器',
'使用': '敏感数据(银行记录等)'
},
'private': {
'含义': '只能在浏览器缓存,不能在代理服务器缓存',
'使用': '用户个人信息等'
},
'public': {
'含义': '可以被任何节点缓存(浏览器、CDN、代理)',
'使用': '静态公共资源'
},
'must-revalidate': {
'含义': '缓存过期后必须验证有效性',
'使用': '避免使用过期缓存'
},
's-maxage=86400': {
'含义': '在代理服务器中的缓存时间(覆盖 max-age)',
'使用': 'CDN 缓存时间控制'
}
};
const expiresExample = `
Expires: Mon, 12 May 2026 23:59:59 GMT
`;
const lastModifiedFlow = `
请求:
GET /api/users HTTP/1.1
响应:
HTTP/1.1 200 OK
Last-Modified: Mon, 12 May 2026 10:00:00 GMT
Content-Type: application/json
{users: [...]}
---
再次请求:
GET /api/users HTTP/1.1
If-Modified-Since: Mon, 12 May 2026 10:00:00 GMT
如果资源未变化:
HTTP/1.1 304 Not Modified
(无响应体)
如果资源已变化:
HTTP/1.1 200 OK
Last-Modified: Mon, 12 May 2026 11:00:00 GMT
{users: [...]} // 新内容
`;
const etagFlow = `
请求:
GET /api/users HTTP/1.1
响应:
HTTP/1.1 200 OK
ETag: "abc123"
Content-Type: application/json
{users: [...]}
---
再次请求:
GET /api/users HTTP/1.1
If-None-Match: "abc123"
如果资源未变化:
HTTP/1.1 304 Not Modified
(无响应体)
如果资源已变化:
HTTP/1.1 200 OK
ETag: "def456"
{users: [...]} // 新内容
`;
const cacheCompare = {
'Last-Modified': {
'优点': '简单,HTTP 协议内置',
'缺点': '时间精度到秒,短时间内变化可能检测不到'
},
'ETag': {
'优点': '精确检测(基于内容哈希),支持弱 ETag',
'缺点': '计算哈希有性能开销'
}
};
const cacheFlow = `
浏览器请求资源
|
v
检查强缓存(Cache-Control / Expires)
|
| 命中
v
直接使用本地缓存(200 from memory/disk cache)
|
| 未命中
v
发送请求到服务器,带上协商缓存头
If-None-Match / If-Modified-Since
|
v
检查协商缓存
|
| 命中(资源未变化)
v
304 Not Modified,使用本地缓存
|
| 未命中(资源已变化)
v
200 OK,返回新内容
更新本地缓存
`;
const cacheDecision = {
'请求到达': '检查是否有缓存',
'否': '向服务器请求',
'是': '检查缓存新鲜度',
'新鲜': '直接使用(强缓存命中)',
'不新鲜': '发起协商缓存请求',
'服务器确认未变': '304,使用缓存',
'服务器确认已变': '200,返回新内容'
};
const express = require('express');
const app = express();
app.get('/static/*', (req, res) => {
res.set('Cache-Control', 'public, max-age=31536000');
});
app.get('/api/data', (req, res) => {
const data = getData();
const lastModified = data.lastModified;
const etag = data.etag;
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
if (req.headers['if-modified-since'] === lastModified) {
return res.status(304).end();
}
res.set({
'Last-Modified': lastModified,
'ETag': etag,
'Cache-Control': 'private, max-age=3600'
});
res.json(data);
});
app.get('/api/realtime', (req, res) => {
res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
res.json(getRealtimeData());
});
const userActions = {
'F5 / 刷新按钮': {
'行为': '跳过强缓存,检查协商缓存',
'原因': '用户想看到最新内容'
},
'Ctrl + F5': {
'行为': '跳过所有缓存,重新请求',
'结果': 'Cache-Control: no-cache'
},
'地址栏回车': {
'行为': '正常走缓存策略'
},
'新窗口打开链接': {
'行为': '检查是否有对应的缓存(根据 URL)'
},
'后退按钮': {
'行为': '使用缓存(跳过强缓存检查)',
'原因': '用户刚访问过,应该是最新或足够新'
}
};
五、WebSocket 与长连接
5.1 WebSocket 协议
const wsVsHttp = {
'连接方式': {
'HTTP': '请求-响应模式,客户端发起,服务器被动响应',
'WebSocket': '全双工通信,双方都可以主动发送消息'
},
'建立连接': {
'HTTP': '客户端发请求,服务器返回响应,连接关闭',
'WebSocket': '通过 HTTP 升级(Upgrade)建立持久连接'
},
'数据格式': {
'HTTP': '文本或二进制,有请求头开销',
'WebSocket': '帧格式,最小 2 字节头,效率高'
},
'适用场景': {
'HTTP': 'REST API、文件上传下载、简单查询',
'WebSocket': '实时聊天、在线协作、游戏、推送通知'
}
};
const wsOpcodes = {
'0x0': 'Continuation frame(延续帧,用于分割大消息)',
'0x1': 'Text frame(文本帧)',
'0x2': 'Binary frame(二进制帧)',
'0x8': 'Close frame(关闭连接)',
'0x9': 'Ping frame(心跳检测)',
'0xA': 'Pong frame(响应心跳)'
};
class WebSocketClient {
constructor(url) {
this.ws = new WebSocket('wss://api.example.com/ws');
this.ws.onopen = (event) => {
console.log('WebSocket 连接已建立');
this.ws.send(JSON.stringify({ type: 'subscribe', channel: 'notifications' }));
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
this.ws.onclose = (event) => {
console.log('WebSocket 连接关闭', event.code, event.reason);
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
}
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
close(code = 1000, reason = '') {
this.ws.close(code, reason);
}
reconnect() {
setTimeout(() => {
console.log('尝试重新连接...');
this.ws = new WebSocket('wss://api.example.com/ws');
}, 3000);
}
}
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', (ws, req) => {
const clientIP = req.socket.remoteAddress;
ws.send('欢迎连接 WebSocket');
ws.on('message', (message) => {
console.log('收到消息:', message.toString());
const data = JSON.parse(message.toString());
switch (data.type) {
case 'ping':
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
break;
case 'broadcast':
server.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(data.message);
}
});
break;
}
});
const pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping();
}
}, 30000);
ws.on('close', () => {
clearInterval(pingInterval);
console.log('客户端断开连接');
});
ws.on('error', (error) => {
console.error('WebSocket 错误:', error);
});
});
class HeartbeatWebSocket {
constructor(url, options = {}) {
this.url = url;
this.options = options;
this.reconnectInterval = options.reconnectInterval || 3000;
this.heartbeatInterval = options.heartbeatInterval || 30000;
this.ws = null;
this.timer = null;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('连接建立');
this.startHeartbeat();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'pong') {
console.log('心跳响应正常');
}
};
this.ws.onclose = () => {
console.log('连接断开');
this.stopHeartbeat();
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('连接错误');
};
}
startHeartbeat() {
this.timer = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
}
}, this.heartbeatInterval);
}
stopHeartbeat() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
reconnect() {
setTimeout(() => {
console.log('尝试重连...');
this.connect();
}, this.reconnectInterval);
}
}
六、CORS 跨域
6.1 CORS 原理与实现
const simpleRequestConditions = [
'请求方法:GET、POST、HEAD',
'Content-Type:application/x-www-form-urlencoded、multipart/form-data、text/plain',
'无自定义请求头(如 Authorization)',
'请求中没有使用 ReadableStream 对象'
];
const simpleCorsFlow = `
浏览器 -> 服务器:发送请求(含 Origin 头)
Origin: https://example.com
服务器 -> 浏览器:返回响应(含 Access-Control-Allow-Origin 头)
Access-Control-Allow-Origin: https://example.com
浏览器检查是否允许,如果不允许,抛出错误
`;
const preflightCorsFlow = `
浏览器 -> 服务器:预检请求(OPTIONS)
Origin: https://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
服务器 -> 浏览器:预检响应
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 // 预检结果缓存 24 小时
浏览器 -> 服务器:实际请求(带 Origin)
服务器 -> 浏览器:实际响应(含 Access-Control-Allow-Origin)
`;
const corsResponseHeaders = {
'Access-Control-Allow-Origin': {
'示例': 'https://example.com 或 *',
'说明': '允许的来源,* 表示所有来源(不建议)'
},
'Access-Control-Allow-Methods': {
'示例': 'GET, POST, PUT, DELETE, OPTIONS',
'说明': '允许的 HTTP 方法'
},
'Access-Control-Allow-Headers': {
'示例': 'Content-Type, Authorization, X-Requested-With',
'说明': '允许的请求头'
},
'Access-Control-Allow-Credentials': {
'示例': 'true',
'说明': '是否允许携带 Cookie(需配合前端 withCredentials)'
},
'Access-Control-Max-Age': {
'示例': '86400',
'说明': '预检结果缓存时间(秒)'
},
'Access-Control-Expose-Headers': {
'示例': 'X-Total-Count, X-Page-Number',
'说明': '允许浏览器访问的响应头'
}
};
const express = require('express');
const cors = require('cors');
app.use(cors());
app.use(cors({
origin: 'https://example.com',
origin: (req, callback) => {
const allowed = ['https://example.com', 'https://api.example.com'];
callback(null, allowed.includes(req.header('Origin')));
},
credentials: true,
maxAge: 86400
}));
app.use((req, res, next) => {
const origin = req.headers.origin;
const allowedOrigins = ['https://example.com', 'https://api.example.com'];
if (allowedOrigins.includes(origin)) {
res.set({
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Credentials': 'true'
});
}
if (req.method === 'OPTIONS') {
return res.status(204).end();
}
next();
});
fetch('/api/users')
.then(res => res.json())
.then(data => console.log(data));
fetch('/api/users', {
credentials: 'include'
})
.then(res => {
return res.json();
});
fetch('/api/users', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({ name: 'Alice' })
});
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
};
const corsVsJsonp = {
'CORS': {
'支持方法': 'GET、POST、PUT、DELETE 等所有方法',
'支持头': '支持自定义请求头',
'安全性': '支持 Cookie、Authorization',
'错误处理': '有完整的错误信息',
'兼容性': 'IE10+'
},
'JSONP': {
'支持方法': '只支持 GET',
'支持头': '不支持自定义请求头',
'安全性': '不安全(动态脚本注入)',
'错误处理': '难以捕获错误',
'兼容性': '支持老浏览器(如 IE6、IE7)'
}
};
6.2 跨域解决方案
const crossDomainSolutions = {
'CORS': {
'原理': '服务器设置允许跨域的响应头',
'前端': '正常发请求,无需额外处理',
'后端': '设置 Access-Control-Allow-Origin 等头',
'适用': 'REST API、XMLHttpRequest/fetch'
},
'JSONP': {
'原理': '利用 script 标签不受同源限制',
'前端': '回调函数 + script 标签',
'后端': '返回函数调用字符串',
'适用': 'GET 请求,老浏览器',
'缺点': '只支持 GET,不安全'
},
'代理服务器': {
'原理': '同源代理请求到目标服务器',
'前端': '请求同源代理',
'后端': '代理服务器转发请求',
'适用': '开发环境、服务器配置有限'
},
'WebSocket': {
'原理': 'WebSocket 不受同源限制',
'前端': 'new WebSocket(wsUrl)',
'后端': '正常处理 WebSocket 连接',
'适用': '实时通信'
},
'postMessage': {
'原理': 'window.postMessage 跨窗口通信',
'前端': '发送:iframe.contentWindow.postMessage',
'后端': '监听:window.addEventListener(message)',
'适用': '页面嵌套 iframe 通信'
},
'window.name': {
'原理': 'window.name 在页面跳转时保持',
'前端': '设置 name,通过 iframe 跳转传递',
'后端': '无需修改',
'适用': '特定场景(历史遗留)'
}
};
function jsonp(url, params = {}, callbackName = 'callback') {
return new Promise((resolve, reject) => {
const callbackId = `jsonp_${Date.now()}_${Math.random().toString(36).substr(2)}`;
const queryString = Object.entries({ ...params, [callbackName]: callbackId })
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
const fullUrl = `${url}${url.includes('?') ? '&' : '?'}${queryString}`;
const script = document.createElement('script');
script.src = fullUrl;
window[callbackId] = (data) => {
delete window[callbackId];
document.body.removeChild(script);
resolve(data);
};
script.onerror = () => {
delete window[callbackId];
document.body.removeChild(script);
reject(new Error('JSONP request failed'));
};
document.body.appendChild(script);
});
}
function sendMessage(targetWindow, message, targetOrigin) {
targetWindow.postMessage(message, targetOrigin);
}
window.addEventListener('message', (event) => {
if (event.origin !== 'https://expected-origin.com') {
return;
}
const { type, data } = event.data;
switch (type) {
case 'notification':
console.log('收到通知:', data);
break;
case 'response':
console.log('收到响应:', data);
break;
}
});
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage({ type: 'ping' }, 'https://child-origin.com');
window.addEventListener('message', (event) => {
if (event.origin !== 'https://parent-origin.com') return;
if (event.data.type === 'ping') {
event.source.postMessage({ type: 'pong' }, event.origin);
}
});
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq, req) => {
if (req.method === 'OPTIONS') {
proxyReq.setHeader('Access-Control-Allow-Origin', req.headers.origin);
proxyReq.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
proxyReq.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
}
});
}
}
}
}
});
七、网络安全
7.1 XSS 与 CSRF
function sanitizeInput(input) {
return input
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function csrfTokenExample() {
function generateToken() {
return crypto.randomBytes(32).toString('hex');
}
function verifyToken(req, res, next) {
const token = req.body._csrf || req.headers['x-csrf-token'];
const sessionToken = req.session.csrfToken;
if (token === sessionToken) {
next();
} else {
res.status(403).json({ error: 'Invalid CSRF token' });
}
}
}
const sameSiteOptions = {
'Strict': {
'说明': '完全禁止跨域携带 Cookie',
'缺点': '用户体验差(跨站导航无法携带状态)'
},
'Lax': {
'说明': '允许顶级导航(地址栏变化)携带 Cookie',
'适用': '大多数场景',
'示例': '点击链接跳转可以携带,跳转iframe不可以'
},
'None': {
'说明': '允许跨域携带(需配合 Secure)',
'使用': '需要跨域 API 的场景',
'注意': '必须配合 Secure(HTTPS)'
}
};
function verifyOrigin(req) {
const origin = req.headers.origin;
const referer = req.headers.referer;
if (origin && !allowedOrigins.includes(origin)) {
return false;
}
if (referer && !allowedDomains.some(d => referer.startsWith(d))) {
return false;
}
return true;
}
const securityChecklist = {
'XSS': {
'防御': ['输入过滤', '输出编码', 'CSP', 'HttpOnly'],
'工具': ['DOMPurify', 'sanitize-html']
},
'CSRF': {
'防御': ['CSRF Token', 'SameSite Cookie', '验证 Origin'],
'框架': ['csurf (Express)']
},
'SQL注入': {
'防御': ['参数化查询', 'ORM', '输入验证'],
'工具': ['SQLAlchemy', 'Hibernate']
},
'点击劫持': {
'防御': ['X-Frame-Options', 'CSP frame-ancestors'],
'示例': 'DENY / SAMEORIGIN'
},
'密码存储': {
'防御': ['bcrypt', 'argon2', '盐值'],
'禁止': ['明文存储', 'MD5/SHA1 单次哈希']
},
'HTTPS': {
'防御': ['强制 HTTPS', 'HSTS', '安全 Cookie'],
'配置': ['HTTPS only', 'Strict-Transport-Security']
}
};
const securityHeaders = {
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'Access-Control-Allow-Origin': '具体域名,不是 *'
};
const helmet = require('helmet');
app.use(helmet());
7.2 HTTP/HTTPS 安全配置
const cspConfig = {
'default-src': "'self'",
'script-src': "'self' 'unsafe-inline' https://cdn.example.com",
'style-src': "'self' 'unsafe-inline'",
'img-src': "'self' data: https:",
'font-src': "'self' https://fonts.gstatic.com",
'connect-src': "'self' https://api.example.com",
'frame-src': "'none'",
'object-src': "'none'"
};
const hstsConfig = {
'max-age': 31536000,
'includeSubDomains': true,
'preload': true
};
const xFrameOptions = {
'DENY': '禁止被嵌入任何 iframe',
'SAMEORIGIN': '只允许同源 iframe',
'ALLOW-FROM uri': '允许特定来源(已废弃)'
};
'X-Content-Type-Options: nosniff'
const referrerPolicy = {
'no-referrer': '不发送 Referer',
'no-referrer-when-downgrade': 'HTTPS->HTTP 不发送(默认)',
'origin': '只发送源(协议+主机+端口)',
'origin-when-cross-origin': '跨域时只发送源',
'same-origin': '同源才发送',
'strict-origin-when-cross-origin': '严格模式',
'unsafe-url': '始终发送完整 URL(不安全)'
};
const httpsBestPractices = {
'证书选择': {
'类型': '至少 DV,建议 OV 或 EV',
'算法': 'RSA 2048+ 或 ECDSA 256+',
'域名': '覆盖所有需要的域名和子域名'
},
'TLS 版本': {
'最低': 'TLS 1.2',
'推荐': 'TLS 1.3',
'禁止': 'SSL 3.0, TLS 1.0, TLS 1.1'
},
'cipher_suites': [
'TLS_AES_256_GCM_SHA384',
'TLS_AES_128_GCM_SHA256',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
],
'配置项': {
'证书透明度': '启用 CT 日志',
'OCSP Stapling': '启用,提升握手性能',
'Session Tickets': '启用,支持会话恢复'
}
};
const https = require('https');
const fs = require('fs');
const options = {
cert: fs.readFileSync('/path/to/cert.pem'),
key: fs.readFileSync('/path/to/key.pem'),
minVersion: 'TLSv1.2',
ciphers: 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256',
};
const server = https.createServer(options, (req, res) => {
res.set({
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Secure response' }));
});
server.listen(443, () => {
console.log('HTTPS server running on port 443');
});
八、网络性能优化
8.1 资源加载优化
function lazyLoadImages() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, {
rootMargin: '50px 0px'
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
}
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const mergeConfig = {
};
const imageOptimization = {
'格式选择': {
'JPEG': '照片、复杂图像',
'PNG': '需要透明、图标',
'WebP': '现代浏览器,支持压缩比更高',
'AVIF': '最新格式,压缩比最高',
'SVG': '图标、简单图形'
},
'响应式图片': `
<img srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
src="medium.jpg" alt="描述">
`,
'CDN': '使用 CDN 加速图片加载'
};
8.2 HTTP/2 与 HTTP/3
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
cert: fs.readFileSync('cert.pem'),
key: fs.readFileSync('key.pem')
});
server.on('stream', (stream, headers) => {
if (headers[':path'] === '/') {
stream.pushStream({ ':path': '/styles.css' }, (err, pushStream) => {
pushStream.respond({
':status': 200,
'content-type': 'text/css'
});
pushStream.end('body { margin: 0; }');
});
stream.respond({
':status': 200,
'content-type': 'text/html'
});
stream.end('<html><head><link rel="stylesheet" href="/styles.css"></head><body>Test</body></html>');
}
});
const http3vs2 = {
'传输层': {
'HTTP/2': 'TCP + TLS 1.2/1.3',
'HTTP/3': 'UDP + QUIC(内置 TLS 1.3)'
},
'队头阻塞': {
'HTTP/2': 'TCP 层队头阻塞(丢包影响所有流)',
'HTTP/3': 'QUIC 层解决,无队头阻塞'
},
'连接建立': {
'HTTP/2': '1-3 个 RTT(TCP + TLS)',
'HTTP/3': '0-1 个 RTT(0-RTT、1-RTT)'
},
'迁移': {
'HTTP/2': 'IP 变化需要重建连接',
'HTTP/3': '连接 ID 支持无缝迁移'
}
};
const http2ResourceStrategy = {
'不再需要域名分片': 'HTTP/2 多路复用,不需要多个域名',
'合并文件可能不再必要': 'HTTP/2 可以并发请求多个小文件',
'小文件内联': '减少请求数,但增加主文件大小',
'服务器推送利用': '让服务器主动推送关键资源'
};
九、网络面试高频问题
9.1 三次握手四次挥手
9.2 HTTPS 连接过程
9.3 HTTP 缓存
9.4 WebSocket vs HTTP
十、高频面试清单
网络必背清单
| 类别 | 题目 | 难度 | 重点 |
|---|
| HTTP | 请求/响应报文结构 | ⭐ | 各部分作用 |
| HTTP | HTTP 方法区别 | ⭐ | GET/POST/PUT/DELETE |
| HTTP | 状态码分类 | ⭐ | 2xx/3xx/4xx/5xx |
| HTTP | 持久连接 vs 管线化 | ⭐⭐ | HTTP/1.1 特性 |
| HTTPS | TLS 握手过程 | ⭐⭐ | 混合加密原理 |
| HTTPS | 为什么不只用非对称加密 | ⭐ | 性能问题 |
| DNS | 解析流程 | ⭐ | 递归查询、迭代查询 |
| DNS | 常见记录类型 | ⭐ | A/CNAME/MX |
| TCP | 三次握手 | ⭐⭐ | 状态变化、为什么三次 |
| TCP | 四次挥手 | ⭐⭐ | 状态变化、为什么四次 |
| TCP | TIME_WAIT | ⭐⭐ | 2MSL 原因 |
| 缓存 | 强缓存 vs 协商缓存 | ⭐⭐ | Cache-Control/ETag |
| 缓存 | 304 何时返回 | ⭐ | 协商缓存命中 |
| 缓存 | 用户操作与缓存 | ⭐ | F5/Ctrl+F5 |
| CORS | 简单请求 vs 预检 | ⭐⭐ | 条件判断 |
| CORS | 响应头含义 | ⭐ | Access-Control-* |
| WebSocket | 握手过程 | ⭐⭐ | HTTP Upgrade |
| WebSocket | 心跳机制 | ⭐ | ping/pong |
| 安全 | XSS 类型与防御 | ⭐⭐ | CSP/输入输出编码 |
| 安全 | CSRF 防御 | ⭐⭐ | Token/SameSite |
| HTTP/2 | 多路复用原理 | ⭐⭐ | 流、帧、ID |
| HTTP/3 | QUIC 优势 | ⭐⭐ | UDP/0-RTT/无队头阻塞 |