Source: 前端常见八股文

工程化

  1. Webpack 性能优化有哪些方法
  2. Webpack 的 loader 和 plugin 区别是什么?常用的 plugin 和 loader 有哪些
  3. Webpack 构建流程是什么
  4. 讲讲 tree-shaking 原理

性能优化

  1. 前端页面性能如何优化
  2. 讲讲回流和重绘的区别,如何避免回流和重绘

安全

  1. 网络攻击有哪些?如何防御

网络通信

  1. 说说从输入 url 到页面展示出来的整个过程
  2. 什么是跨域?为什么会出现跨域?如何解决跨域问题?Jsonp 原理是什么
  3. Http 各版本的改进都是什么
  4. Https 原理是什么?为什么可以保证安全性
  5. Http 常见状态码有哪些
  6. Http 有哪些方法
  7. Get 和 post 区别是什么
  8. 讲讲 http 缓存机制
  9. Cdn 是什么?它的原理是什么
  10. 讲讲 304 协商缓存过程
  11. 浏览器有哪些缓存?LocalStorage、sessionStorage、cookie、session 的区别是什么

Webpack 性能优化有哪些方法

(你的答案)

Webpack 的 loader 和 plugin 区别是什么?常用的 plugin 和 loader 有哪些

Loaders 的核心任务是让 Webpack 能够处理 JavaScript 以外的文件; Plugins 的能力范围比 Loader 广得多,负责构建流程中的各种优化和自动化。

常见的Loader:

  • babel-loader最核心的 Loader。使用 Babel 将 ES6/ES7+ 的现代 JavaScript 语法转换为向后兼容的 ES5 语法。
  • ts-loader: 将 TypeScript (.ts 文件) 转换为 JavaScript。
  • style-loader: 将 css-loader 处理后的 CSS 内容,通过 <style> 标签动态注入到 HTML 的 <head> 中。
  • css-loader解析 CSS 文件,处理 @import 和 url() 等语法,但它只负责解析,不负责应用样式。
  • sass-loader / less-loader: 分别将 Sass/SCSS 或 Less 文件编译成标准的 CSS。
  • file-loader / asset modules (Webpack 5+): 处理图片、字体等文件。它会将文件复制到输出目录,并返回其公共 URL。
  • url-loader: 类似于 file-loader,但它可以将小于指定大小(limit)的文件转换为 Base64 编码的 Data URL,直接嵌入到代码中,从而减少 HTTP 请求。

常见的Plugin

  • HtmlWebpackPlugin几乎是必备插件。它会自动生成一个 HTML 文件,并将 Webpack 打包后的所有 JS/CSS 文件(bundles)自动通过 <script> 和 <link> 标签注入进去。
  • CleanWebpackPlugin: 在每次成功构建前,自动清理 output.path 目录(通常是 dist 文件夹),防止旧的构建文件残留。
  • MiniCssExtractPlugin生产环境必备。它会将 CSS 从 JS 包中提取出来,生成一个独立的 .css 文件。这与 style-loader(将 CSS 注入 JS)的做法相反,可以实现 CSS 和 JS 的并行加载,提升性能。
  • TerserWebpackPlugin: Webpack 5 内置的 JS 代码压缩插件(用于生产模式)。它使用 Terser 来移除注释、空格,并进行代码混淆,以减小最终包的体积。通常无需手动配置,在 mode: 'production' 时会自动启用。
  • DefinePlugin: Webpack 内置插件。允许你创建可在编译时配置的全局常量,非常适合用于区分开发环境和生产环境(例如,定义 process.env.NODE_ENV)。

Webpack 构建流程是什么

(你的答案)

讲讲 tree-shaking 原理

Tree-Shaking 的核心原理

Tree-Shaking 的实现,强依赖于 ES Modules (ESM) 的静态模块结构。这是理解其原理的关键。

ES Modules (ESM) vs. CommonJS

  • ES Modules (import/export)
    • 静态分析:ESM 的导入导出关系在代码编译时就已经确定了,是静态的。你不能在 if 语句里 import,也不能把 import 的路径写成一个变量。
    • 特点import { a } from './utils.js' 这种语法,打包工具(如 Webpack, Rollup)在不运行代码的情况下,通过静态分析就能明确知道:“我只需要 utils.js 模块里的 a,别的我都不需要。”
  • CommonJS (require/module.exports)
    • 动态加载:CommonJS 的模块导入是运行时加载的,是动态的。require 的路径可以是一个变量,它可以在任何代码逻辑中执行。
    • 特点const myModule = require(myPath + '/utils.js')。打包工具在构建时无法确定 myPath 到底是什么,也就无法知道到底加载了什么,更无法判断模块里的哪些代码被使用了。require 返回的是一个包含所有 exports 的完整对象。

Tree-Shaking 的工作流程

Tree-Shaking 的过程通常可以分为两个阶段,以 Webpack 为例:

阶段一:标记 (Marking)

  1. 构建依赖图:Webpack 从入口文件(entry point)开始,根据 import 语句遍历所有模块,构建出一个依赖图(Dependency Graph)
  2. 标记“活”代码:Webpack 遍历依赖图,标记出哪些模块的哪些导出(export)被实际使用了。
    • 所有被 import 并且在代码中被调用的导出,都会被标记为 /* harmony export used */
    • 所有只被 import 但未被使用的导出,不会被标记。
    • 所有从未被 import 的模块和导出,自然也是未被标记的。

阶段二:摇掉 (Shaking / Sweeping)

  1. 代码压缩与删除:这个阶段通常由代码压缩工具(minifier)来完成,比如 Terser (Webpack 5 内置的压缩插件)。
  2. Terser 在压缩代码时,会检查到那些在第一阶段没有被标记为 “used” 的代码。它会认为这些代码是“死代码”(dead code),并在最终生成 bundle 文件时,将它们彻底地从代码中删除。

前端页面性能如何优化

一、 加载优化 (让资源更快到达浏览器)

这是性能优化的第一道关卡,目标是减少网络请求的数量体积

1. 减少资源体积

  • 代码压缩 (Minification):使用工具(如 Terser、cssnano)移除 JS、CSS、HTML 中的空格、注释和不必要的字符。这是构建流程中的标配。
  • 资源压缩 (Compression):在服务器端开启 Gzip 或 Brotli 压缩。Brotli 的压缩率更高,但需要浏览器和服务器都支持。这是性价比最高的优化手段之一。
  • 图片优化
    • 选择合适的格式
      • WebP / AVIF:在同等画质下体积远小于 JPEG/PNG,是现代网页首选。使用 <picture> 标签做兼容性降级。
      • SVG:用于图标和简单的矢量图形,无限缩放且体积小。
    • 图片压缩:使用工具(如 TinyPNG、ImageOptim)或构建插件对图片进行有损或无损压缩。
    • 响应式图片:使用 <img> 标签的 srcset 和 sizes 属性,让浏览器根据屏幕尺寸和分辨率加载最合适大小的图片,避免在小屏上加载大图。
  • 字体优化
    • 字体裁剪:只打包用到的字符(例如,一个网站可能只需要几百个常用汉字)。
    • 使用 WOFF2 格式:这是目前压缩率最高的字体格式。

2. 减少请求数量

  • 代码合并 (Bundling):使用 Webpack、Vite 等构建工具将多个 JS、CSS 文件合并成一个或几个文件,减少 HTTP 请求次数。
  • 按需加载 (Code Splitting):这是现代框架的核心优化。
    • 路由懒加载:只有当用户访问某个页面时,才加载该页面的 JS 和 CSS。
    • 组件懒加载:对于非首屏或需要用户交互后才出现的组件(如弹窗、折叠面板),进行动态 import()

3. 加快请求速度

  • 使用 CDN (Content Delivery Network):将静态资源(JS, CSS, 图片, 字体)部署到 CDN。CDN 会将资源缓存到离用户最近的服务器上,极大缩短网络延迟。
  • 利用浏览器缓存
    • 强缓存 (Cache-Control: max-ageExpires):资源在有效期内,浏览器直接从本地读取,不发请求。适用于不常变化的静态资源。
    • 协商缓存 (ETagLast-Modified):资源过期后,浏览器向服务器发送请求,服务器判断资源是否变化,若未变则返回 304 Not Modified,浏览器继续使用本地缓存。

二、 渲染优化 (让页面更快地绘制出来)

资源到达后,浏览器需要解析、计算并绘制页面。这个过程也需要优化。

  • 懒加载 (Lazy Loading)
    • 图片懒加载:对于非首屏的图片,使用 loading="lazy" 属性(浏览器原生支持)或第三方库,在图片进入视口时才开始加载。
    • iframe 懒加载:同理,使用 loading="lazy"

三、 运行时优化 (让交互更流畅)

页面加载完成后,用户开始交互,此时的性能同样重要。

  • 减少重绘 (Repaint) 和回流 (Reflow)
    • 回流 (Reflow/Layout):当 DOM 元素的几何属性(如宽高、位置)发生变化,浏览器需要重新计算布局。回流非常耗性能。
    • 重绘 (Repaint):当元素的视觉属性(如颜色、背景)发生变化,但不影响布局时,浏览器只需重新绘制。
    • 优化策略
      • 批量修改 DOM:使用 DocumentFragment 或在 display: none 的元素上进行多次操作,最后一次性添加到 DOM 中。
      • 避免频繁读取布局属性:将读取(如 el.offsetWidth)和写入操作分离,不要混在一起。
      • 使用 CSS Transform 和 Opacity 实现动画transform 和 opacity 的变化通常可以被 GPU 加速,不会触发回流和重绘,性能远高于使用 top/left
  • 使用节流 (Throttle) 和防抖 (Debounce)
    • 防抖 (Debounce):对于高频触发的事件(如输入框搜索),在用户停止操作一段时间后才执行一次回调。
    • 节流 (Throttle):对于高频触发的事件(如 scrollresize),保证在固定时间间隔内只执行一次回调。
  • 使用 Web Workers:对于复杂的、计算密集型的任务(如数据处理、加解密),可以将其放到 Web Worker 中执行,避免阻塞主线程,从而保持 UI 的响应性。
  • 虚拟列表 (Virtual List):对于需要渲染成千上万条数据的长列表,只渲染视口内和缓冲区内的列表项,极大提升渲染性能和滚动流畅度。

四、 工具与监控 (科学地度量和分析)

没有度量,就没有优化。

  • 使用性能分析工具
    • Chrome DevTools
      • Lighthouse:提供全面的性能审计报告和优化建议,关注 Core Web Vitals
      • Performance 面板:录制页面运行时,生成火焰图,用于深入分析 JS 执行瓶颈、渲染性能等。
      • Network 面板:分析资源加载情况,查看瀑布图、缓存状态等。
  • 关注核心 Web 指标 (Core Web Vitals)
    • LCP (Largest Contentful Paint):最大内容绘制,衡量加载性能。目标是小于 2.5 秒。
    • FID (First Input Delay) / INP (Interaction to Next Paint):首次输入延迟/下次绘制交互,衡量交互性。目标是小于 100 毫秒。
    • CLS (Cumulative Layout Shift):累积布局偏移,衡量视觉稳定性。目标是小于 0.1。

讲讲回流和重绘的区别,如何避免回流和重绘

回流也被成为重排,以下用 重排 来称呼;

引用一下我觉得说的很好的一段话:

“重排和重绘是浏览器关键渲染路径上的两个节点, 浏览器的关键渲染路径就是 DOM 和 CSSOM 生成渲染树,然后根据渲染树通过一个布局(也叫 layout)步骤来确定页面上所有内容的大小和位置,确定布局后,将像素 绘制 (也叫 Paint)到屏幕上。 其中重排就是当元素的位置发生变动的时候,浏览器重新执行布局这个步骤,来重新确定页面上内容的大小和位置,确定完之后就会进行重新绘制到屏幕上,所以重排一定会导致重绘。 如果元素位置没有发生变动,仅仅只是样式发生变动,这个时候浏览器重新渲染的时候会跳过布局步骤,直接进入绘制步骤,这就是重绘,所以重绘不一定会导致重排。” from https://cloud.tencent.com/developer/article/1967594

触发回流 (Reflow) 的常见操作:

  1. 页面首次渲染:这是不可避免的一次。
  2. DOM 节点操作:添加、删除、修改 DOM 节点。
  3. 元素几何属性变化:修改 widthheightmarginpaddingborderlefttop 等。
  4. 元素内容变化:文本数量或图片大小的改变,导致元素尺寸变化。
  5. 浏览器窗口尺寸变化 (resize)
  6. 获取某些布局属性:这是一个非常隐蔽的触发点。当你用 JavaScript 读取像 offsetWidthoffsetHeightscrollTopclientWidthgetComputedStyle() 等属性时,浏览器为了给你一个精确的、最新的值,必须立即执行一次回流操作,以确保所有布局都是最新的。

仅触发重绘 (Repaint) 的常见操作:

  • 修改 colorbackground-coloroutlinebox-shadowvisibility 等不影响布局的样式。

如何避免或减少回流和重绘?

优化的核心思想是:减少回流的次数和范围

1. CSS 层面

  • 使用 transform 和 opacity 实现动画
    • 这是最重要的优化手段transform (位移、缩放、旋转) 和 opacity (透明度) 的变化不会触发回流和重绘。浏览器会将这些元素的渲染提升到一个独立的“合成层”(Compositor Layer),并交由 GPU 去处理。这被称为“硬件加速”。
    • 反例:使用 top/left 或 margin 来实现位移动画,会在每一帧都触发回流,性能极差。
  • 避免使用 table 布局
    • 表格中一个微小的改动就可能导致整个表格的回流。Flexbox 和 Grid 布局在性能上表现更优。
  • 将频繁回流的元素设置为 position: absolute 或 fixed
    • 这样可以将该元素脱离文档流,它的回流就不会影响到页面上的其他元素,从而限制了回流的范围。

2. JavaScript 层面

  • 批量修改 DOM
  • 对于复杂的动画,使用requestAnimationFrame

网络攻击有哪些?如何防御

1. XSS (Cross-Site Scripting, 跨站脚本攻击)

这是前端领域最常见、最知名的攻击。

  • 攻击原理
    攻击者将恶意的 JavaScript 脚本注入到网页中,当其他用户访问这个网页时,这些脚本就会在用户的浏览器中执行。攻击者可以利用这些脚本窃取用户的 Cookie、Session、LocalStorage,或者伪装成用户进行操作(如发帖、转账)。

    • 存储型 XSS:恶意脚本被存储在服务器的数据库中(例如,在一篇博客文章或用户评论里),对所有访问该页面的用户造成影响。
    • 反射型 XSS:恶意脚本存在于 URL 中(例如 https://example.com?q=<script>alert('xss')</script>),用户点击这个恶意链接时,脚本被执行。通常用于钓鱼攻击。
    • DOM 型 XSS:攻击不经过服务器,而是通过修改 URL 的片段,由前端 JavaScript 直接获取并执行,从而触发。
  • 防御措施

    1. 输出编码 (Output Encoding)这是最核心、最有效的防御手段。当需要将用户输入的内容展示在页面上时,对其进行 HTML 编码。将特殊字符(如 <>"')转换为 HTML 实体(&lt;&gt;&quot;&#39;)。现代前端框架(如 React, Vue)默认都会进行输出编码。
    2. 输入验证 (Input Validation):对用户输入的数据格式进行校验,例如,年龄字段只能是数字,用户名只能是字母和数字。但这只能作为辅助手段,不能完全防止 XSS。
    3. 内容安全策略 (Content Security Policy, CSP):通过设置 HTTP 头部 Content-Security-Policy,可以限制浏览器只加载和执行来自可信来源的脚本,有效防止未知脚本的执行。
    4. 设置 HttpOnly Cookie:给关键的 Cookie 设置 HttpOnly 属性,这样 JavaScript 就无法读取到该 Cookie,即使发生 XSS 攻击,攻击者也无法轻易窃取用户的会话信息。

2. CSRF (Cross-Site Request Forgery, 跨站请求伪造)

  • 攻击原理
    攻击者诱导已登录的用户访问一个恶意网站(evil.com)。在这个恶意网站上,攻击者放置了向你的网站(bank.com)发送请求的代码(例如,一个隐藏的表单或一个 <img> 标签)。因为用户在 bank.com 是登录状态,浏览器会自动携带 bank.com 的 Cookie 发送这个请求,从而在用户不知情的情况下,以用户的名义执行了恶意操作(如转账、修改密码)。

    核心是:攻击者利用了用户的登录状态,伪造了用户的请求。

  • 防御措施

    1. 使用 Anti-CSRF Token:在用户访问表单页面时,服务器生成一个随机的、不可预测的 Token,并将其放在表单的隐藏字段和用户的 Session 中。当用户提交表单时,服务器验证表单中的 Token 是否与 Session 中的一致。攻击者无法获取这个 Token,因此无法伪造请求。这是最传统的防御方式。
    2. 使用 SameSite Cookie 属性:这是更现代、更简单的防御方式。在设置 Cookie 时,将其 SameSite 属性设置为 Strict 或 Lax
      • SameSite=Strict:完全禁止第三方 Cookie,浏览器在任何跨站请求中都不会携带该 Cookie。
      • SameSite=Lax:在大多数跨站请求中不发送 Cookie,但在一些安全的导航操作(如点击链接跳转)中会发送。这足以防御大部分 CSRF 攻击。
    3. 验证 Referer/Origin 头:检查 HTTP 请求头中的 Referer 或 Origin 字段,确保请求来自可信的源。但这个头部可以被伪造,所以只能作为辅助防御手段。

3. SQL 注入 (SQL Injection)

  • 攻击原理
    当应用程序将用户输入的内容直接拼接到 SQL 查询语句中时,攻击者可以通过构造恶意的输入,来改变原始 SQL 语句的语义,从而执行非预期的数据库操作,如窃取数据、删除数据,甚至获取服务器的控制权。

    例如,一个登录查询:SELECT * FROM users WHERE username = ' + username + ' AND password = ' + password + '
    如果攻击者输入用户名为 admin' --,密码随意,SQL 语句就变成了 SELECT * FROM users WHERE username = 'admin' --' AND ...-- 是 SQL 的注释符,直接绕过了密码验证。

  • 防御措施

    1. 使用参数化查询 (Parameterized Queries) 或预编译语句 (Prepared Statements)这是防止 SQL 注入的唯一可靠方法。它将 SQL 命令和用户数据分开发送给数据库。数据库引擎会把用户输入的数据当作纯粹的“数据”,而不是“命令”,从而从根本上杜绝了注入的可能。
    2. 使用 ORM (Object-Relational Mapping):现代的 ORM 框架(如 Sequelize, TypeORM, Hibernate)通常默认使用参数化查询,能有效防止 SQL 注入。
    3. 对输入进行转义:作为最后的防线,可以对用户输入的特殊字符进行转义,但这非常容易出错,不推荐作为主要防御手段。

4. DDoS (Distributed Denial of Service, 分布式拒绝服务攻击)

  • 攻击原理
    攻击者控制大量的“僵尸网络”(被病毒感染的计算机),同时向目标服务器发送海量的无效或高负荷的请求,耗尽服务器的带宽、CPU、内存等资源,使其无法响应正常用户的请求,导致服务瘫痪。

  • 防御措施
    DDoS 防御通常不是靠应用层代码能完全解决的,更多依赖于网络和基础设施层面。

    1. 使用 CDN (Content Delivery Network):CDN 可以将流量分散到全球各地的节点,其巨大的带宽和分布式架构可以吸收和清洗大量的攻击流量。
    2. 使用 WAF (Web Application Firewall):专业的硬件或云 WAF 可以识别并拦截恶意的流量模式。
    3. 流量清洗服务:购买专业的 DDoS 防护服务,在流量到达你的服务器之前就进行清洗。
    4. 负载均衡:将流量分发到多个服务器,避免单点故障。
    5. 应用层限流 (Rate Limiting):限制单个 IP 地址在单位时间内的请求次数。

5. 点击劫持 (Clickjacking)

  • 攻击原理
    攻击者创建一个透明的 <iframe>,覆盖在一个看似无害的网页上(例如,一个“点击领奖”的按钮)。<iframe> 中加载的是你的网站(例如,一个删除账户的页面)。当用户点击“领奖”按钮时,实际上点击的是透明 <iframe> 中的“确认删除”按钮,从而在不知情的情况下执行了危险操作。

  • 防御措施

    1. 设置 X-Frame-Options HTTP 头:这是一个专门用来防止点击劫持的头部。
      • DENY:禁止任何页面通过 <iframe> 嵌入该页面。
      • SAMEORIGIN:只允许同源的页面嵌入。
    2. 使用 CSP 的 frame-ancestors 指令:这是 X-Frame-Options 的现代替代品,功能更强大,可以指定允许嵌入的来源。Content-Security-Policy: frame-ancestors 'self' example.com;

说说从输入 url 到页面展示出来的整个过程

整个过程可以概括为:

  1. URL 解析与检查:浏览器处理用户输入。
  2. 网络请求:浏览器通过网络协议栈向服务器发送请求。
  3. 服务器响应:服务器处理请求并返回数据。
  4. 浏览器渲染:浏览器将返回的数据渲染成用户可见的页面。

重点讲一下浏览器渲染,这也被称为关键渲染路径

  1. 解析 HTML,构建 DOM 树 (DOM Tree)
    • 浏览器接收到 HTML 文件后,会自上而下地进行解析,将 HTML 标签转换成一个树状结构的 DOM (Document Object Model)
  2. 解析 CSS,构建 CSSOM 树 (CSSOM Tree)
    • 在解析 HTML 的过程中,如果遇到 <link rel="stylesheet"> 或 <style> 标签,浏览器会开始异步下载并解析 CSS 文件,构建一个树状的 CSSOM (CSS Object Model),它包含了所有元素的样式信息。
  3. 执行 JavaScript
    • 如果解析时遇到 <script> 标签(且没有 async 或 defer 属性),HTML 解析会暂停。浏览器会下载、解析并执行 JavaScript 代码。
    • 为什么会暂停? 因为 JavaScript 可能会修改 DOM(例如 document.write),所以浏览器必须先执行完 JS,才能确定后续的 DOM 结构。这就是为什么通常建议将 <script> 放在 <body> 底部的原因
    • 如果把<script>放入<head>中,最核心的影响就是,他会阻塞页面的渲染
  4. 构建渲染树 (Render Tree)
    • 当 DOM 树和 CSSOM 树都构建完毕后,浏览器会将它们结合起来,生成 渲染树 (Render Tree)
    • 渲染树只包含需要被渲染的节点(例如,display: none 的节点不会在渲染树中)。它包含了每个节点的样式信息。
  5. 布局 (Layout / Reflow)
    • 浏览器根据渲染树,计算出每个节点在屏幕上的确切位置和大小。这个过程称为布局回流重排
  6. 绘制 (Painting / Repaint)
    • 布局完成后,浏览器会调用 GPU,将渲染树中的每个节点绘制到屏幕上,变成我们最终看到的像素。这个过程称为绘制重绘
  7. 后续加载与交互
    • 主 HTML 文件渲染完成后,浏览器会继续加载页面中引用的其他资源,如图片、字体等。
    • 当这些资源加载完成并显示出来,同时 JavaScript 也执行完毕(例如绑定了事件监听器),页面就变得完全可交互了。

什么是跨域?为什么会出现跨域?如何解决跨域问题?Jsonp 原理是什么

跨域(Cross-Origin) 指的是一个源(Origin) 的网页脚本试图去请求另一个源的资源,但被浏览器限制了。

要理解跨域,首先必须理解同源策略(Same-Origin Policy)

同源策略是浏览器内置的一个核心安全功能。它规定,只有当两个页面的协议(Protocol)、域名(Domain)和端口(Port) 三者完全相同时,才算作“同源”。

URL是否同源原因
http://www.example.com/api/data协议、域名、端口都相同(80是http默认端口)
https://www.example.com/api/data协议不同 (http vs https)
http://api.example.com/api/data域名不同 (www.example.com vs api.example.com)
http://www.example.com:8080/api/data端口不同 (80 vs 8080)
http://www.another.com/api/data域名不同

如何解决跨域问题?

解决跨域问题的核心思想是:让目标服务器明确告知浏览器,我允许这个源来访问我的资源。 以下是几种主流的解决方案:

1. CORS (Cross-Origin Resource Sharing) - 跨域资源共享(主流推荐)

这是目前最标准、最主流的解决方案。它的核心在于服务器端的配置。

  • 工作原理:浏览器在发送跨域请求时,会自动在请求头中添加一个 Origin 字段,表明请求来自哪个源。服务器收到请求后,根据这个 Origin 值判断是否允许该请求。如果允许,服务器会在响应头(Response Header)中添加一个 Access-Control-Allow-Origin 字段,其值为允许的源(或者是 * 代表允许所有源)。浏览器收到响应后,会检查这个响应头,如果发现自己的源被允许,就正常处理响应;否则,就会拦截响应,并在控制台报错。

2. JSONP (JSON with Padding) - (历史悠久,但有局限)

这是早期常用的一种“投机取巧”的跨域方案

3. 代理服务器 (Proxy)

这是一种非常实用且灵活的方案,尤其在开发环境中。

  • 工作原理:利用服务器之间通信不受同源策略限制的特点。

    1. 前端将请求发送到自己的同源服务器(这个服务器就是代理服务器)。
    2. 代理服务器再将这个请求转发给目标服务器
    3. 目标服务器处理完请求,将响应返回给代理服务器。
    4. 代理服务器最后将响应返回给前端。

    在整个过程中,前端与代理服务器是同源的,浏览器不会拦截。而服务器与服务器之间的通信不存在跨域问题。

  • 实现

    • 开发环境:现代前端框架的脚手架(如 Vue CLI, Create React App)都内置了代理功能(devServer.proxy)。
    • 生产环境:通过 Nginx 或其他 Web 服务器配置反向代理。

Http 各版本的改进都是什么

版本核心改进解决了什么问题引入了什么新问题
HTTP/1.0引入头信息、状态码、多方法实现了丰富的 Web 交互短连接,性能低下
HTTP/1.1持久连接、Host头、缓存解决了短连接的开销问题应用层队头阻塞 (HOL Blocking)
HTTP/2多路复用、二进制分帧、头部压缩,服务器推送解决了应用层的队头阻塞TCP 层的队头阻塞
HTTP/3基于 QUIC (UDP)彻底解决了 TCP 层的队头阻塞推广和网络设备支持需要时间

Https 原理是什么?为什么可以保证安全性

HTTPS 的全称是 HyperText Transfer Protocol Secure,它本质上就是 HTTP + SSL/TLS。SSL/TLS 是一个安全层,负责对 HTTP 传输的数据进行加密和身份认证。

HTTPS 如何保证安全性?三大核心目标

HTTPS 主要通过解决以下三个问题来保证通信安全:

  1. 机密性 (Confidentiality):防止信息在传输过程中被第三方窃听。所有数据都经过加密,即使被截获,窃听者看到的也只是一堆无意义的乱码。
  2. 完整性 (Integrity):防止信息在传输过程中被篡改。通过数字签名等机制,接收方可以验证数据是否在途中被修改过。如果被改动,会立刻被发现。
  3. 身份认证 (Authentication):验证通信对方的身份,确保你正在访问的是真实的网站(如真实的银行网站),而不是一个伪造的“钓鱼”网站。

HTTPS 的工作原理:SSL/TLS 握手过程

HTTPS 的安全性核心在于其连接建立时的“握手 (Handshake)”过程。这个过程非常精妙,结合了非对称加密对称加密的优点。

  • 非对称加密:有公钥和私钥两把钥匙。公钥加密的数据只能用对应的私钥解密。优点是安全(私钥不暴露),缺点是计算复杂,速度慢。
  • 对称加密:加密和解密用同一把钥匙。优点是速度快,缺点是如何安全地把这把钥匙交给对方是个难题。

第 1 步:客户端发起请求 (Client Hello)

你的浏览器(客户端)向服务器发起请求,并告诉服务器:

  • 我支持哪些 SSL/TLS 版本。
  • 我支持哪些加密算法(称为 Cipher Suite)。
  • 我生成了一个随机数 (Client Random)

第 2 步:服务器响应 (Server Hello)

服务器收到请求后,会回应:

  • 确认使用的 SSL/TLS 版本和加密算法。
  • 生成了另一个随机数 (Server Random)
  • 发送自己的数字证书 (Digital Certificate)。这是最关键的一步!

什么是数字证书?
这个证书由权威的 CA (Certificate Authority) 机构颁发,里面包含了:

  • 网站的域名
  • 证书的有效期
  • 网站的公钥
  • CA 机构的数字签名(用来证明这个证书是真的)

第 3 步:客户端验证与密钥交换

浏览器收到证书后,会进行验证:

  1. 验证证书真伪
    • 检查证书是否在有效期内。
    • 检查证书的域名与正在访问的域名是否一致。
    • 使用浏览器内置的、受信任的 CA 机构的公钥,去解密证书上的“CA 数字签名”。如果能成功解密,就证明这个证书确实是这个权威 CA 颁发的,而不是伪造的。
  2. 生成会话密钥
    • 如果证书验证通过,浏览器会再生成一个随机数 (Pre-Master Secret)
    • 然后,浏览器用从证书里拿到的服务器公钥,将这个 Pre-Master Secret 加密。
    • 将加密后的内容发送给服务器。

第 4 步:服务器解密与生成会话密钥

  • 服务器收到加密后的 Pre-Master Secret 后,用自己的私钥进行解密,获取到原始的 Pre-Master Secret
  • 现在,客户端和服务器同时拥有了三个相同的凭据Client RandomServer Random 和 Pre-Master Secret
  • 双方使用约定好的算法,将这三个随机数混合在一起,生成一个完全相同的、用于后续通信的“会话密钥 (Session Key)”。这是一个对称密钥

第 5 步:握手完成,开始加密通信

  • 握手阶段结束。
  • 从现在开始,客户端和服务器之间的所有 HTTP 数据,都使用这个刚刚生成的会话密钥进行对称加密后传输。因为对称加密速度非常快,所以不影响通信性能。

HTTPS 如何实现三大安全目标

  1. 身份认证:通过第 3 步的数字证书验证,浏览器确认了服务器的真实身份,防止了中间人伪装成目标服务器。
  2. 机密性
    • 在握手阶段,用于生成会话密钥的 Pre-Master Secret 是用服务器的公钥加密的,只有服务器的私钥能解开,保证了会话密钥本身的安全传递
    • 握手后,所有通信内容都由会话密钥进行对称加密,即使被截获也无法破解。
  3. 完整性:在发送加密数据时,会附带一个消息认证码 (MAC),这个 MAC 是由原始数据和会话密钥计算得出的。接收方在解密后,会用同样的方法计算 MAC,如果两个 MAC 一致,说明数据未被篡改。

Http 常见状态码有哪些

HTTP 状态码被分为五大类,通过第一个数字来区分:

  • 1xx (信息性):表示服务器已接收请求,需要客户端继续执行操作。
  • 2xx (成功):表示请求已成功被服务器接收、理解、并接受。
  • 3xx (重定向):表示需要客户端采取进一步的操作才能完成请求。
  • 4xx (客户端错误):表示客户端的请求有语法错误或请求无法实现。
  • 5xx (服务器错误):表示服务器在处理请求的过程中发生了错误。
类别常见代码名称简单描述
2xx 成功200OK请求成功
201Created已创建新资源
204No Content成功,但无内容返回
3xx 重定向301Moved Permanently永久重定向
302Found临时重定向
304Not Modified资源未修改,使用缓存
4xx 客户端错误400Bad Request请求语法错误
401Unauthorized未认证(需要登录)
403Forbidden已认证,但无权限
404Not Found资源不存在
5xx 服务器错误500Internal Server Error服务器内部错误
502Bad Gateway网关错误(后端服务挂了)
504Gateway Timeout网关超时(后端服务响应慢)

Http 有哪些方法

方法主要用途安全性幂等性常见场景
GET查询资源✅ 安全✅ 幂等浏览网页、获取数据
POST创建资源❌ 不安全❌ 不幂等提交表单、上传文件
PUT整体更新或创建资源❌ 不安全✅ 幂等更新完整的个人资料
DELETE删除资源❌ 不安全✅ 幂等删除文章、注销账号
PATCH局部更新资源❌ 不安全❌ 不一定只修改某个字段
HEAD获取资源的元信息(头部)✅ 安全✅ 幂等检查文件大小
OPTIONS查询服务器支持的方法✅ 安全✅ 幂等CORS 预检请求

Get 和 post 区别是什么

  • GET (Get - 获取):其核心语义是从服务器获取资源。它是一个只读操作。
  • POST (Post - 提交):其核心语义是向服务器提交数据,以创建或更新资源。它会改变服务器的状态。
对比维度GETPOST
数据位置参数附加在 URL 的查询字符串中,在 ? 之后。参数放在 HTTP 请求体 (Request Body) 中。
数据可见性可见。数据会显示在浏览器地址栏、浏览器历史记录和服务器日志中。不可见。数据不会显示在地址栏,更适合传输敏感信息。
数据大小有限制。因为 URL 长度受限于浏览器和服务器,通常在 2KB 到 8KB 之间。无限制。理论上可以传输任意大小的数据(受服务器配置影响)。
安全性 (Safe)GET 请求不应该改变服务器上的任何资源。POST 请求通常会改变服务器上的资源(如创建新用户)。
幂等性 (Idempotent)。同一个 GET 请求执行多次,结果应该完全相同。。同一个 POST 请求执行多次,会创建多个资源(如多次提交订单)。
可缓存性可缓存。浏览器可以缓存 GET 请求的响应,以提高性能。不可缓存。默认情况下,POST 请求的响应不会被缓存。
浏览器行为- 可以被收藏为书签
- 会被保存在浏览器历史记录中。
- 按后退/刷新按钮无害。
- 不可被收藏为书签。
- 刷新页面时,浏览器会警告“是否重新提交表单”,防止重复提交。
主要用途查询数据。如搜索、获取文章列表、查看商品详情。提交数据。如用户登录、注册、提交订单、上传文件。

讲讲 http 缓存机制

(你的答案)

Cdn 是什么?它的原理是什么

CDN 的全称是 Content Delivery Network,中文翻译为“内容分发网络”。

举个生活中的例子:
假设你在上海,想买一本只在美国纽约一家出版社印刷的书。

  • 没有 CDN 的情况:你下单后,书必须从纽约的仓库,通过飞机、海关、国内物流,长途跋涉送到你上海的家中。这个过程非常慢,而且运费昂贵。
  • 有 CDN 的情况:这家出版社很聪明,它提前把热门书籍的库存,分发到了全球各大城市的“前置仓库”,比如在上海就有一个。你下单后,书直接从上海的仓库发货,第二天就能收到。

在这个例子里:

  •  -> 互联网用户
  • 纽约的出版社 -> 网站的源服务器 (Origin Server)
  • 书籍 -> 网站的内容 (Content),如图片、视频、CSS、JS 文件等
  • 全球的前置仓库 -> 就是 CDN 的节点服务器 (Edge Node)

所以,CDN 的本质是一个由分布在世界各地的服务器组成的、智能化的网络系统。它的核心目标是,让用户能够从离他们地理位置最近的服务器上获取所需内容,从而提高访问速度、减少延迟,并分担源服务器的压力。

工作流程

现在,一个在北京的用户想要访问 example.com 网站。

  1. 用户发起请求:用户的浏览器开始加载网页,当它需要请求一个静态资源时(比如 http://static.example.com/logo.png),它需要先知道这个域名的 IP 地址。

  2. 智能 DNS 解析 (关键步骤):这是 CDN 的“大脑”。

    • 用户的浏览器向本地 DNS 服务器发起对 static.example.com 的解析请求。
    • 本地 DNS 服务器会把这个请求转发给 CDN 服务商的权威 DNS 服务器
    • CDN 的 DNS 服务器不是简单地返回一个固定的 IP 地址。它会进行智能判断:
      • 判断请求来源:它会分析发起请求的 DNS 服务器的 IP 地址,从而大致判断出用户的地理位置(比如,这个请求来自北京的某个运营商)。
      • 选择最佳节点:根据用户的位置、各个边缘节点的健康状况、负载情况以及网络拥堵情况,它会选择一个综合最优的边缘节点。对于北京的用户,它很可能会选择一个位于北京或天津的 CDN 节点。
      • 返回 IP 地址:最后,它将这个最优节点的 IP 地址返回给用户的浏览器。
  3. 获取内容

    • 用户的浏览器拿到了这个最优节点的 IP 地址(比如,北京节点的 IP)。
    • 浏览器直接向这个北京的 CDN 节点发起对 logo.png 的请求。
  4. 响应请求

    • 缓存命中 (Cache Hit):如果北京节点已经缓存了 logo.png 这个文件,它会立刻将文件返回给用户。这个过程极快,因为物理距离非常近。
    • 缓存未命中 (Cache Miss):如果这是第一次有该区域的用户请求这个文件,北京节点上可能还没有缓存。这时,北京节点会向源服务器example.com 的主服务器)发起请求,获取 logo.png 文件。拿到文件后,它会做两件事:
      1. 将文件存储在自己的缓存中,以便下次该区域有用户请求时可以直接返回(变为 Cache Hit)。
      2. 将文件发送给当前请求的用户。

讲讲 304 协商缓存过程

见下

浏览器有哪些缓存?LocalStorage、sessionStorage、cookie、session 的区别是什么

浏览器的缓存机制

广义上讲,浏览器的缓存是为了提升网站性能和用户体验,将已经获取过的资源(如 HTML、CSS、JS、图片等)或数据存储在本地,以便下次访问时可以更快地加载。

主要可以分为以下几类:

1. HTTP 缓存 (Browser Cache)

这是最主要的缓存类型,它根据 HTTP 响应头中的字段来决定如何缓存资源。它又分为两种:

  • 强缓存 (Strong Cache):浏览器在请求资源时,会先检查本地缓存。如果缓存未过期,则不向服务器发送任何请求,直接从本地读取资源,HTTP 状态码为 200 (from memory cache) 或 200 (from disk cache)
    • 控制字段
      • Expires (HTTP/1.0):一个绝对时间的 GMT 格式字符串,表示缓存的过期时间。
      • Cache-Control: max-age=xxx (HTTP/1.1):一个相对时间,单位是秒,表示资源在被缓存后的 xxx 秒内有效。优先级高于 Expires
  • 协商缓存 (Negotiation Cache):当强缓存失效后(或者 Cache-Control 设置为 no-cache),浏览器会向服务器发送一个请求,但会带上本地缓存的标识。服务器根据这个标识判断资源是否有更新。
    • 如果无更新,服务器返回 304 Not Modified 状态码,并且响应体为空。浏览器继续使用本地缓存。
    • 如果有更新,服务器返回 200 OK 状态码,并附带最新的资源。
    • 控制字段(成对出现):
      • Last-Modified / If-Modified-Since:服务器在响应中告诉浏览器资源的最后修改时间 (Last-Modified)。浏览器下次请求时会带上这个时间 (If-Modified-Since),服务器进行比较。
      • ETag / If-None-Match:服务器为资源生成一个唯一的标识符 (ETag)。浏览器下次请求时会带上这个标识符 (If-None-Match),服务器进行比较。ETag 的优先级高于 Last-Modified,因为它能解决文件修改时间变了但内容没变等问题。

2. Web Storage (HTML5)

这是 HTML5 提供的客户端数据存储方案,用于存储键值对数据。它解决了 Cookie 存储量小且会随请求发送的问题。

  • LocalStorage:永久性存储,除非用户手动清除或代码主动删除,否则数据一直存在。
  • SessionStorage:会话级存储,数据仅在当前浏览器标签页的生命周期内有效。关闭标签页或浏览器后,数据被清除。

在 Web Storage 出现之前,Cookie 是主要的客户端存储方案。它主要用于服务器和客户端之间的通信。

4. 其他缓存

  • Service Worker Cache:一个更现代、更强大的缓存机制。它允许开发者通过 JavaScript 精确控制缓存策略,拦截网络请求,实现离线应用(PWA 的核心技术)。
  • IndexedDB:一个在客户端存储大量结构化数据的数据库,可以看作是浏览器端的 NoSQL 数据库。适用于复杂的应用数据存储。
  • Memory Cache:内存中的缓存,速度极快但生命周期短。当标签页关闭时,缓存就会被清除。通常用于缓存解码后的图片、脚本等。
  • Push Cache:HTTP/2 的特性,服务器可以主动将资源“推送”到浏览器缓存中。

LocalStorage、sessionStorage、Cookie、Session 的区别

其中,Session 是一个纯服务端的概念,但它通常依赖于客户端的 Cookie 来维持状态,所以我们放在一起比较。

特性CookieLocalStorageSessionStorageSession
存储位置客户端客户端客户端服务端
生命周期可设置过期时间。若不设置,则随浏览器关闭而失效;若设置,则在过期时间前一直有效。永久有效,除非用户手动清除或代码删除。当前会话/标签页有效,关闭标签页或浏览器后即失效。服务端控制,通常在用户关闭浏览器或一段时间无活动后失效(超时)。
存储大小约 4KB,非常小。约 5-10MB,较大。约 5-10MB,较大。无限制,取决于服务器的内存或硬盘。
与服务器通信每次 HTTP 请求都会自动携带在请求头中,即使是请求图片等静态资源。从不自动发送给服务器,需要通过代码手动发送。从不自动发送给服务器,需要通过代码手动发送。数据本身在服务端,仅通过一个 Session ID (通常存在 Cookie 中) 与客户端关联。
作用域同一浏览器内,所有符合其 path 和 domain 规则的页面共享。同一源(协议、域名、端口)下的所有标签页和窗口共享仅在单个标签页内有效,不同标签页之间不共享。在服务端与特定用户(通过 Session ID)关联。
API 易用性原生 API (document.cookie) 比较简陋,需要自己封装。原生 API (setItemgetItemremoveItem) 非常简单易用。原生 API 与 LocalStorage 相同。客户端 JS 无法直接访问,由服务端代码操作。
安全性容易受到 CSRF、XSS 攻击。可设置 HttpOnly 属性防止 JS 读取,提升安全性。容易受到 XSS 攻击。容易受到 XSS 攻击。相对最安全,因为核心数据存储在服务端,客户端只存一个无意义的 ID。

总结与应用场景

  • Cookie:
    • 场景:主要用于维持用户登录状态。服务器验证用户后,生成一个 Session ID 存入 Cookie,之后客户端每次请求都带上这个 Cookie,服务器以此识别用户。也用于用户行为跟踪(如 Google Analytics)。
    • 优点:兼容性好,可设置 HttpOnly 防止脚本读取。
    • 缺点:大小限制,性能开销(每次请求都携带)。
  • LocalStorage:
    • 场景:需要长期在本地存储大量数据的场景,且这些数据不常与服务器交互。例如:网站主题偏好(夜间模式)、用户自定义配置、缓存不常变动的应用数据、存储 JWT Token 等。
    • 优点:容量大,API 简单,不影响网络性能。
    • 缺点:永久存储可能导致数据过时,需要手动管理。
  • SessionStorage:
    • 场景临时存储单个标签页的数据,用完即走。例如:在多步骤表单中,临时保存用户已填写的信息,防止刷新页面后丢失;或者在单页应用中保存当前页面的某些临时状态。
    • 优点:API 简单,数据隔离性好(不同标签页不干扰),关闭即焚,无需手动清理。
    • 缺点:生命周期短,无法跨标签页共享。
  • Session:
    • 场景:存储敏感、重要的用户信息。例如:用户的登录状态、权限信息、购物车内容等。
    • 优点安全性高,数据存储在服务端,客户端无法篡改。
    • 缺点:占用服务器资源,需要维护 Session ID 的传递(通常依赖 Cookie)。H)**:你输入的命令和服务器返回的结果,都必须准确无误。

选择 UDP 的场景 (实时性/速度优先)

当对实时性要求极高,可以容忍少量数据丢失时,UDP 是更好的选择。

  • 在线视频/直播 (Streaming):如果为了等一个丢失的数据包(比如画面中的一帧)而让整个视频卡顿,用户体验会非常糟糕。宁愿丢失一帧(画面出现瞬间花屏),也要保证流畅播放。
  • 网络电话 (VoIP):和视频同理,实时通话的流畅性远比偶尔丢失一个声音采样点更重要。
  • 在线游戏:玩家的位置、动作等信息需要被快速发送。如果一个数据包延迟到达,它包含的已经是过时的信息,不如直接丢弃,使用最新的数据包。
  • DNS (域名系统):查询一个域名的 IP 地址,这个过程要求非常快。通常只是一次简单的请求和响应,如果查询失败(数据包丢失),客户端简单地重试一次即可,使用 UDP 开销最小。

前端面试常见题-网络和工程化篇

https://www.yuque.com/baiyueguang-rfnbu/tr4d0i/rz15kr
作者

MeorinLime 梦灵

发布日期

2025 - 08 - 11