Skip to content

[toc]

一、 浏览器安全:XSS 与 CSRF

1. XSS (跨站脚本攻击)

本质:恶意脚本注入。攻击者利用网站对用户输入过滤不严的漏洞,将脚本注入页面,在用户浏览器中执行

  • 存储型:最危险。恶意脚本存入服务器数据库(如评论区),每个访问用户都会中招
  • 反射型:脚本在 URL 参数中。攻击者诱导用户点击恶意链接(如搜索结果页)
  • DOM 型:纯前端漏洞。JS 直接取出 URL 中的数据并执行(如 innerHTML),不经过服务器

防御策略

  1. 转义/过滤:对所有用户输入进行 HTML 转义(如 < 转为 <
  2. CSP (内容安全策略):通过 HTTP 头或 Meta 标签建立域名白名单,限制资源加载
  3. HttpOnly:禁止 JS 读取关键 Cookie,防止 Token 被盗

2. CSRF (跨站请求伪造)

本质:冒充用户发起请求。利用浏览器在同源请求中自动携带 Cookie 的特性,在用户不知情的情况下操作其账户

防御策略

  1. 验证码:强制人机交互(体验较差)
  2. CSRF Token:服务器生成随机 Token,前端请求时手动带上,服务器比对
  3. SameSite Cookie:设置 SameSite=Strict/Lax,限制第三方站点携带 Cookie
  4. Referer/Origin 检查:检查请求来源是否为合法域名

二、 进程与线程

1. 核心概念

  • 进程:资源分配的最小单位(如:一个 Chrome 浏览器实例)
  • 线程:CPU 调度的最小单位(如:渲染进程里的 JS 引擎线程)

2. Chrome 的多进程架构

现代浏览器采用多进程架构,主要为了稳定性(一个插件崩溃不影响整个浏览器)和安全性(沙箱隔离)

  • 浏览器主进程:控制界面、子进程管理
  • 网络进程:加载网络资源
  • 渲染进程:每个 Tab 一个(默认),内含 GUI 渲染线程JS 引擎线程(两者互斥)、事件触发线程等

三、 浏览器标签页间通信 (Cross-Tab Communication)

由于同源策略限制,标签页间无法直接交流,必须通过“中介”:

  1. LocalStorage:利用 storage 事件监听变化
  2. BroadcastChannel:专门用于同源页面间广播消息的 API
  3. SharedWorker:多个标签页共享同一个后台线程
  4. Service Worker:作为代理中转
  5. Window.postMessage:如果能获取窗口句柄(如 window.open 返回的对象),可跨域通信

四、 死锁 (Deadlock)

定义:多个进程因争夺资源而造成的相互等待、无法推进的僵局

产生条件 (缺一不可)

  1. 互斥:资源只能被一个进程占用
  2. 请求与保持:占着碗里的,盯着锅里的
  3. 不可剥夺:资源不能被强行抢走
  4. 环路等待:A 等 B,B 等 C,C 等 A

预防方案:破坏上述任一条件。例如资源有序分配法,按编号申请资源,避免形成环路

五、浏览器缓存

1. 缓存决策全流程

当浏览器向服务器请求资源(如 JS, CSS, 图片)时,会经历以下决策树:

  1. 强缓存阶段:检查 Cache-Control。如果资源没过期(在 max-age 时间内),直接从本地内存(Memory Cache)或磁盘(Disk Cache)读取。状态码 200 (from cache)
  2. 协商缓存阶段:如果强缓存过期了,浏览器会带上“令牌”(EtagLast-Modified)去问服务器:“我这儿有份旧的,还能用吗?”
  3. 服务器判定
    • 没变:服务器返回 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. 协商缓存

当强缓存失效,浏览器会通过以下两对“暗号”进行验证:

  1. Etag / If-None-Match (优先级高)
  • 原理:服务器根据文件内容生成的唯一哈希值(指纹)。内容变了,指纹必变。
  • 优势:最精确。解决了 Last-Modified 只能精确到秒的问题。
  1. Last-Modified / If-Modified-Since (优先级低)
  • 原理:记录文件的最后修改时间
  • 缺点:如果文件在 1 秒内修改了多次,或者文件只是打开存了一下但内容没变,它也会失效

六、浏览器本地存储:Cookie、Storage 与 IndexedDB

1. 三种主流方案对比

特性CookieLocalStorageSessionStorage
容量4KB 左右5MB 或更大5MB 或更大
时效性ExpiresMax-Age 决定永久,除非手动删除会话级,标签页关闭即消失
请求携带每次 HTTP 请求都会携带不参与通信不参与通信
数据共享同源窗口共享同源窗口共享仅限当前标签页
主要用途身份验证(SessionID)持久化设置(如换肤、登录信息)临时表单数据、单次浏览记录

2. IndexedDB:浏览器里的数据库

当存储数据量超过 50MB 甚至更高,且需要复杂的检索(如索引、事务)时,IndexedDB 是最佳选择。它是一个非关系型数据库(NoSQL),支持二进制存储

七、同源策略

同源策略 (Same-Origin Policy) 是浏览器最核心的安全机制

  • 定义:如果两个 URL 的 协议 (Protocol)域名 (Domain)端口 (Port) 完全一致,则称之为同源
  • 限制范围
    1. 无法读取非同源的 Cookie、LocalStorage、IndexedDB
    2. 无法操作非同源的 DOM
    3. AJAX/Fetch 请求不能跨域(除非服务器允许)

八、如何解决跨域问题

跨域不是“无法请求”,而是“浏览器拦截了响应”。以下是几种主流的解决手段:

1. CORS (跨源资源共享)

这是最常用、最正规的方案

  • 简单请求:直接发送请求,服务器返回 Access-Control-Allow-Origin
  • 复杂请求 (PUT/DELETE/自定义头):浏览器先发一个 OPTIONS 预检请求,确认安全后再发真实请求

复杂请求流程:

  • 预检请求 (OPTIONS): 浏览器先发一个方法名为 OPTIONS 的请求,问服务器:“我能用这个方法和这个 Header 访问吗?”
  • 服务端响应: 返回允许的 Origin、Methods 和 Headers
  • 正式请求: 只有预检通过,浏览器才会发送你真正的业务数据

发了预请求之后

一旦 OPTIONS 请求到达服务器并返回了 204 No Content(或 200 OK),浏览器会进行以下操作:

  1. 浏览器端的验证

​ 浏览器会比对 OPTIONS 响应头中的字段:

  • Access-Control-Allow-Origin: 是否包含当前的域名?
  • Access-Control-Allow-Methods: 是否允许你接下来的请求方法(如 PUT)?
  • Access-Control-Allow-Headers: 是否允许你携带的自定义 Header?
  1. 正式请求的发送

如果验证通过:浏览器会自动发起第二次请求(真正的 POST/GET 等)。这次请求会携带真实的业务参数(JSON、Body 等)。 如果验证失败:浏览器控制台会直接报错(CORS Error),真正的业务请求根本不会发出去

  1. 性能优化:缓存预检 (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. 浏览器事件模型:捕获与冒泡

当你在页面上点击一个按钮时,浏览器并不是只触发了那个按钮的事件,而是经历了一个完整的“往返”过程

  1. 三个阶段
  • 捕获阶段 (Capture):事件从最高层的 Window 一直向下传递到目标元素
  • 目标阶段 (Target):事件到达真正被点击的元素
  • 冒泡阶段 (Bubbling):事件从目标元素开始,逐级向上传递回 Window
  1. 阻止传播
  • event.stopPropagation():阻止事件继续向上冒泡或向下捕获
  • event.stopImmediatePropagation():不仅阻止冒泡,还阻止该元素上绑定的其他相同类型的事件监听函数执行

2. 事件委托

事件委托 (Event Delegation) 是利用“事件冒泡”原理,将子元素的监听器绑定到父元素上

  1. 为什么需要它?
  • 节省内存:如果有 1000 个 li,不需要创建 1000 个监听函数,只需要在 ul 上绑定 1 个
  • 动态性:后续动态添加的子元素,无需重新绑定事件,父级依然能捕获
  1. 局限性
  • 并不是所有事件都冒泡,例如 focusblurloadscroll 是不冒泡的,无法使用事件委托

3. 执行栈 (Execution Stack)

执行栈(也叫调用栈)是 JavaScript 引擎追踪函数执行顺序的地方。它遵循 “先进后出 (LIFO)” 的原则

  • 当执行一个函数时,它的执行上下文会被压入栈顶
  • 当函数执行完毕,它会从栈顶弹出
  • 爆栈 (Stack Overflow):如果递归没有出口,栈空间被撑满,浏览器就会报错

4. 事件循环 (Event Loop)

因为 JS 是单线程的,为了不让耗时操作(如网络请求)阻塞 UI,JS 使用了事件循环机制

宏任务 vs 微任务

任务被分为两个队列:

  • 微任务 (Microtask):优先级高。包括 Promise.thenMutationObserverprocess.nextTick
  • 宏任务 (Macrotask):优先级低。包括 setTimeoutsetIntervalI/O、整个 script 脚本、UI 渲染

执行顺序(必背流程)

  1. 执行当前的同步代码(这属于第一个宏任务)
  2. 同步代码执行完,检查并执行完所有的微任务
  3. (如有必要)执行浏览器 UI 渲染
  4. 取出下一个宏任务执行,然后重复步骤 2 和 3

5. 同步与异步的执行判断

javascript
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):生产环境错误采集