[toc]
一、 浏览器安全:XSS 与 CSRF
1. XSS (跨站脚本攻击)
本质:恶意脚本注入。攻击者利用网站对用户输入过滤不严的漏洞,将脚本注入页面,在用户浏览器中执行
- 存储型:最危险。恶意脚本存入服务器数据库(如评论区),每个访问用户都会中招
- 反射型:脚本在 URL 参数中。攻击者诱导用户点击恶意链接(如搜索结果页)
- DOM 型:纯前端漏洞。JS 直接取出 URL 中的数据并执行(如
innerHTML),不经过服务器
防御策略:
- 转义/过滤:对所有用户输入进行 HTML 转义(如
<转为<) - CSP (内容安全策略):通过 HTTP 头或 Meta 标签建立域名白名单,限制资源加载
- HttpOnly:禁止 JS 读取关键 Cookie,防止 Token 被盗
2. CSRF (跨站请求伪造)
本质:冒充用户发起请求。利用浏览器在同源请求中自动携带 Cookie 的特性,在用户不知情的情况下操作其账户
防御策略:
- 验证码:强制人机交互(体验较差)
- CSRF Token:服务器生成随机 Token,前端请求时手动带上,服务器比对
- SameSite Cookie:设置
SameSite=Strict/Lax,限制第三方站点携带 Cookie - Referer/Origin 检查:检查请求来源是否为合法域名
二、 进程与线程
1. 核心概念
- 进程:资源分配的最小单位(如:一个 Chrome 浏览器实例)
- 线程:CPU 调度的最小单位(如:渲染进程里的 JS 引擎线程)
2. Chrome 的多进程架构
现代浏览器采用多进程架构,主要为了稳定性(一个插件崩溃不影响整个浏览器)和安全性(沙箱隔离)
- 浏览器主进程:控制界面、子进程管理
- 网络进程:加载网络资源
- 渲染进程:每个 Tab 一个(默认),内含 GUI 渲染线程、JS 引擎线程(两者互斥)、事件触发线程等
三、 浏览器标签页间通信 (Cross-Tab Communication)
由于同源策略限制,标签页间无法直接交流,必须通过“中介”:
- LocalStorage:利用
storage事件监听变化 - BroadcastChannel:专门用于同源页面间广播消息的 API
- SharedWorker:多个标签页共享同一个后台线程
- Service Worker:作为代理中转
- Window.postMessage:如果能获取窗口句柄(如
window.open返回的对象),可跨域通信
四、 死锁 (Deadlock)
定义:多个进程因争夺资源而造成的相互等待、无法推进的僵局
产生条件 (缺一不可):
- 互斥:资源只能被一个进程占用
- 请求与保持:占着碗里的,盯着锅里的
- 不可剥夺:资源不能被强行抢走
- 环路等待:A 等 B,B 等 C,C 等 A
预防方案:破坏上述任一条件。例如资源有序分配法,按编号申请资源,避免形成环路
五、浏览器缓存
1. 缓存决策全流程
当浏览器向服务器请求资源(如 JS, CSS, 图片)时,会经历以下决策树:
- 强缓存阶段:检查
Cache-Control。如果资源没过期(在max-age时间内),直接从本地内存(Memory Cache)或磁盘(Disk Cache)读取。状态码 200 (from cache) - 协商缓存阶段:如果强缓存过期了,浏览器会带上“令牌”(
Etag或Last-Modified)去问服务器:“我这儿有份旧的,还能用吗?” - 服务器判定:
- 没变:服务器返回 304 Not Modified,不传文件体,节省带宽
- 变了:服务器返回 200 OK,并附带最新的文件和“新令牌”
2. 强缓存:本地直接做主
强缓存不需要经过服务器
- Expires (HTTP/1.0):服务器返回的一个绝对时间(如:Thu, 01 Dec 2026 16:00:00 GMT)。
- 缺点:受限于客户端本地时间,如果用户手动改了电脑时间,缓存就会乱套
- Cache-Control (HTTP/1.1):使用相对时间(如:
max-age=3600,表示 1 小时内有效)。它是目前的标准
关键指令解读
- no-cache`:名字有误导性。它不是不缓存,而是“不直接使用强缓存”,每次必须去服务器跑一趟协商缓存。
no-store:真正的禁止缓存。本地不存,服务器每次都重传
3. 协商缓存
当强缓存失效,浏览器会通过以下两对“暗号”进行验证:
- Etag / If-None-Match (优先级高)
- 原理:服务器根据文件内容生成的唯一哈希值(指纹)。内容变了,指纹必变。
- 优势:最精确。解决了
Last-Modified只能精确到秒的问题。
- Last-Modified / If-Modified-Since (优先级低)
- 原理:记录文件的最后修改时间。
- 缺点:如果文件在 1 秒内修改了多次,或者文件只是打开存了一下但内容没变,它也会失效
六、浏览器本地存储:Cookie、Storage 与 IndexedDB
1. 三种主流方案对比
| 特性 | Cookie | LocalStorage | SessionStorage |
|---|---|---|---|
| 容量 | 4KB 左右 | 5MB 或更大 | 5MB 或更大 |
| 时效性 | 由 Expires 或 Max-Age 决定 | 永久,除非手动删除 | 会话级,标签页关闭即消失 |
| 请求携带 | 每次 HTTP 请求都会携带 | 不参与通信 | 不参与通信 |
| 数据共享 | 同源窗口共享 | 同源窗口共享 | 仅限当前标签页 |
| 主要用途 | 身份验证(SessionID) | 持久化设置(如换肤、登录信息) | 临时表单数据、单次浏览记录 |
2. IndexedDB:浏览器里的数据库
当存储数据量超过 50MB 甚至更高,且需要复杂的检索(如索引、事务)时,IndexedDB 是最佳选择。它是一个非关系型数据库(NoSQL),支持二进制存储
七、同源策略
同源策略 (Same-Origin Policy) 是浏览器最核心的安全机制
- 定义:如果两个 URL 的 协议 (Protocol)、域名 (Domain) 和 端口 (Port) 完全一致,则称之为同源
- 限制范围:
- 无法读取非同源的 Cookie、LocalStorage、IndexedDB
- 无法操作非同源的 DOM
- AJAX/Fetch 请求不能跨域(除非服务器允许)
八、如何解决跨域问题
跨域不是“无法请求”,而是“浏览器拦截了响应”。以下是几种主流的解决手段:
1. CORS (跨源资源共享)
这是最常用、最正规的方案
- 简单请求:直接发送请求,服务器返回
Access-Control-Allow-Origin - 复杂请求 (PUT/DELETE/自定义头):浏览器先发一个 OPTIONS 预检请求,确认安全后再发真实请求
复杂请求流程:
- 预检请求 (OPTIONS): 浏览器先发一个方法名为
OPTIONS的请求,问服务器:“我能用这个方法和这个 Header 访问吗?” - 服务端响应: 返回允许的 Origin、Methods 和 Headers
- 正式请求: 只有预检通过,浏览器才会发送你真正的业务数据
发了预请求之后
一旦 OPTIONS 请求到达服务器并返回了 204 No Content(或 200 OK),浏览器会进行以下操作:
- 浏览器端的验证
浏览器会比对 OPTIONS 响应头中的字段:
- Access-Control-Allow-Origin: 是否包含当前的域名?
- Access-Control-Allow-Methods: 是否允许你接下来的请求方法(如
PUT)? - Access-Control-Allow-Headers: 是否允许你携带的自定义 Header?
- 正式请求的发送
如果验证通过:浏览器会自动发起第二次请求(真正的 POST/GET 等)。这次请求会携带真实的业务参数(JSON、Body 等)。 如果验证失败:浏览器控制台会直接报错(CORS Error),真正的业务请求根本不会发出去
- 性能优化:缓存预检 (Access-Control-Max-Age)
每次复杂请求都发两次网络交互(OPTIONS + 真正的请求)非常浪费性能
- 解决方案: 服务端可以在响应
OPTIONS时,加上Access-Control-Max-Age: 86400(单位:秒)。 - 效果: 在这 24 小时内,浏览器针对同一个 URL 的复杂请求,会直接从缓存读取权限结果,不再重复发送
OPTIONSthis绑定规则
2. JSONP
- 原理:利用
<script>标签不受同源策略限制的特性 - 局限:仅支持 GET 请求,且存在 XSS 安全风险
3. Nginx 反向代理
- 原理:同源策略仅针对浏览器。Nginx 作为“中间人”,先接收浏览器的同源请求,再在后端转发给真正的跨域服务器
nginx的作用
Nginx 是一个高性能的 HTTP 服务器和反向代理服务器,常用于网站架构的入口层
五大核心作用
| 作用 | 一句话说明 |
|---|---|
| 静态文件服务器 | 直接托管 HTML/CSS/JS/图片,比动态服务器快 10-50 倍 |
| 反向代理 | 接收客户端请求,转发给后端服务器(Node/Java/Python),隐藏后端细节 |
| 负载均衡 | 将请求分发到多台后端服务器,提升并发能力和可用性 |
| SSL/HTTPS | 统一处理证书、HTTPS 加密,后端服务无需关心 |
| 动静分离 | 静态资源由 Nginx 直接返回,动态请求转发后端,优化性能 |
4. Proxy (代理)
本地开发:直接用 Vite/Webpack 的 proxy 代理配置
原理:前端请求一个“同源”的本地代理服务器,代理服务器再去请求真正的后端,拿到数据后再转发给前端
5. 其他方案
- postMessage:用于不同 Window/Iframe 之间的实时通信
- WebSocket:天然支持跨域的持久化全双工协议
- document.domain:仅限于主域相同、子域不同的场景
九、事件机制、执行栈与事件循环
1. 浏览器事件模型:捕获与冒泡
当你在页面上点击一个按钮时,浏览器并不是只触发了那个按钮的事件,而是经历了一个完整的“往返”过程
- 三个阶段
- 捕获阶段 (Capture):事件从最高层的
Window一直向下传递到目标元素 - 目标阶段 (Target):事件到达真正被点击的元素
- 冒泡阶段 (Bubbling):事件从目标元素开始,逐级向上传递回
Window
- 阻止传播
event.stopPropagation():阻止事件继续向上冒泡或向下捕获event.stopImmediatePropagation():不仅阻止冒泡,还阻止该元素上绑定的其他相同类型的事件监听函数执行
2. 事件委托
事件委托 (Event Delegation) 是利用“事件冒泡”原理,将子元素的监听器绑定到父元素上
- 为什么需要它?
- 节省内存:如果有 1000 个
li,不需要创建 1000 个监听函数,只需要在ul上绑定 1 个 - 动态性:后续动态添加的子元素,无需重新绑定事件,父级依然能捕获
- 局限性
- 并不是所有事件都冒泡,例如
focus、blur、load、scroll是不冒泡的,无法使用事件委托
3. 执行栈 (Execution Stack)
执行栈(也叫调用栈)是 JavaScript 引擎追踪函数执行顺序的地方。它遵循 “先进后出 (LIFO)” 的原则
- 当执行一个函数时,它的执行上下文会被压入栈顶
- 当函数执行完毕,它会从栈顶弹出
- 爆栈 (Stack Overflow):如果递归没有出口,栈空间被撑满,浏览器就会报错
4. 事件循环 (Event Loop)
因为 JS 是单线程的,为了不让耗时操作(如网络请求)阻塞 UI,JS 使用了事件循环机制
宏任务 vs 微任务
任务被分为两个队列:
- 微任务 (Microtask):优先级高。包括
Promise.then、MutationObserver、process.nextTick - 宏任务 (Macrotask):优先级低。包括
setTimeout、setInterval、I/O、整个script脚本、UI 渲染
执行顺序(必背流程)
- 执行当前的同步代码(这属于第一个宏任务)
- 同步代码执行完,检查并执行完所有的微任务
- (如有必要)执行浏览器 UI 渲染
- 取出下一个宏任务执行,然后重复步骤 2 和 3
5. 同步与异步的执行判断
console.log('1'); // 同步
setTimeout(() => {
console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4'); // 同步
// 输出顺序:1 -> 4 -> 3 -> 2- 解析:先跑同步代码
1, 4。跑完后检查微任务队列,发现3,执行它。最后开启下一轮循环,执行宏任务2
十、JWT与Cookie区别
Cookie 是一种存储机制,而 JWT 是一种令牌格式
| 特性 | Cookie (Session 模式) | JWT (Token 模式) |
|---|---|---|
| 存储位置 | 浏览器自动管理(Cookie 存储) | 开发者手动处理 (LocalStorage 或 Cookie) |
| 状态管理 | 有状态。服务端需存储 Session ID | 无状态。服务端不存数据,数据在 Token 中 |
| 数据大小 | 非常小(仅一个 ID) | 较大(包含 Header, Payload, Signature) |
| 跨域支持 | 受同源策略限制,配置较复杂 | 天生支持跨域,适合移动端和多端应用 |
| 安全性 | 需防范 CSRF 攻击 | 需防范 XSS 提取 Token |
| 灵活性 | 只能由浏览器自动发送 | 可以在 Header、URL 或 Body 中手动发送 |
十一、如何排查和优化浏览器问题
1. 分层排查
从外到内、从简单到复杂建立排查路径:
- 网络层:请求是否发出、响应是否正常
- 应用层:JS 是否执行、逻辑是否正确
- 渲染层:DOM 结构、样式计算、布局绘制
- 资源层:加载顺序、缓存策略、体积大小
2. 现象分类
- 页面打不开或白屏:优先检查网络请求、JS 错误、HTML 是否返回
- 点击无反应:检查事件绑定、控制台报错、异步逻辑未执行
- 页面卡顿:主线程长时间占用、频繁重排重绘、内存持续增长
- 样式错乱:CSS 加载失败、选择器优先级、响应式断点未命中
- 数据不更新:状态管理问题、异步时序、缓存未清除
3. 排查工具及用途
- 浏览器开发者工具:
- 控制台:捕获 JS 错误、警告、日志输出
- 网络面板(Network):查看请求耗时、状态码、响应内容、是否被缓存
- 性能面板(Performance):录制运行时性能,分析函数调用栈、重绘区域
- 内存面板(Memory):抓取堆快照,对比内存增长点,定位泄漏
- 应用面板:查看存储(Cookie、LocalStorage、IndexedDB)
- 元素面板:检查 DOM 结构、计算后样式、盒模型
- 光源面板(Lighthouse):综合评分和优化建议
- 抓包工具(Charles、Fiddler):代理、断点、重写请求
- 监控平台(Sentry、Frontend Logging):生产环境错误采集