标签

前端

Web前端开发是从网页制作演变而来的,名称上有很明显的时代特征。在互联网的演化进程中,网页制作是Web 1.0时代的产物,那时网站的主要内容都是静态的,用户使用网站的行为也以浏览为主。2005年以后,互联网进入Web 2.0时代,各种类似桌面软件的Web应用大量涌现,网站的前端由此发生了翻天覆地的变化。网页不再只是承载单一的文字和图片,各种富媒体让网页的内容更加生动,网页上软件化的交互形式为用户提供了更好的使用体验,这些都是基于前端技术实现的。

前端
查看更多相关内容
服务端2026年5月30日 00:10
如何在 axios 中实现请求和响应拦截器?Axios 拦截器就是请求发出前、响应返回后统一插一层处理逻辑。请求拦截器常用来加 token、加请求 ID、处理 loading;响应拦截器常用来拆 `data`、统一处理业务错误、401 登录失效和网络异常。项目里一般不要直接改全局 axios,而是 `axios.create()` 建实例,再给实例挂拦截器,避免多个后端服务互相污染配置。 ## 追问 ### 请求拦截器和响应拦截器分别适合做什么? 请求拦截器改 `config`,比如加 `Authorization`、`baseURL`、防缓存参数。响应拦截器处理 `response` 或 `error`,比如把 `{ code, data, message }` 统一拆成 `data` 返回。 ### 多个拦截器的执行顺序是什么? 请求拦截器后添加的先执行,响应拦截器先添加的先执行。排查问题时要注意顺序,否则 token 还没加上,请求日志就已经打印了旧配置。 ### loading 为什么不能简单请求开始显示、结束隐藏? 并发请求会出问题。第一个请求结束就隐藏 loading,但其他请求还没回来。常见做法是维护计数器,请求加一,响应或错误减一,减到 0 再隐藏。 ### 什么时候要移除拦截器? 临时调试、微前端子应用卸载、某个页面单独加拦截逻辑时要 `eject`,否则重复注册会导致同一个错误提示弹很多次。 ## 写段代码 ```javascript const api = axios.create({ baseURL: '/api', timeout: 10000 }); api.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }); api.interceptors.response.use( res => res.data, err => Promise.reject(err.response?.data || err) ); ```
服务端2026年5月30日 00:10
axios 中如何进行错误处理?有哪些常见错误类型?Axios 错误处理先看三类:`error.response` 表示服务端返回了非 2xx 状态码,重点处理 400、401、403、404、5xx;`error.request` 表示请求发出但没收到响应,多半是网络、超时、CORS;两者都没有通常是请求配置写错。实际项目里建议:业务层只处理当前页面关心的错误,全局拦截器统一做登录失效、错误提示、日志和重试。 ## 追问 ### error.response 和 error.request 有什么区别? `response` 说明后端有响应,只是状态码失败;`request` 说明请求发出去了但没有拿到响应。前者看 `status` 和 `data.message`,后者看 `code`,比如 `ECONNABORTED` 或 `ERR_NETWORK`。 ### 401 应该在每个接口里处理吗? 不要。401 属于全局认证问题,放响应拦截器里统一清 token、跳登录页或刷新 token。页面里只关心业务错误,比如表单校验失败。 ### 超时和 5xx 要不要自动重试? 可以,但只重试幂等请求,比如 GET。POST、支付、下单这类接口不能盲目重试,否则可能造成重复提交。 ### 实际项目里最容易踩什么坑? 拦截器里 `return Promise.reject(error)` 忘了写,外层 catch 拿不到错误;另一个坑是把所有错误都弹 toast,导致 401 跳转时还弹一堆无意义提示。 ## 写段代码 ```javascript axios.interceptors.response.use(r => r.data, error => { if (error.response) { const { status, data } = error.response; if (status === 401) { localStorage.removeItem('token'); location.href = '/login'; } return Promise.reject(new Error(data?.message || `HTTP ${status}`)); } if (error.code === 'ECONNABORTED') { return Promise.reject(new Error('请求超时')); } return Promise.reject(new Error(error.message || '网络错误')); }); ```
服务端5月29日 22:54
axios 如何实现并发请求和取消请求?代码怎么写?axios 的并发和取消分别基于 **Promise.all 和 AbortController/CancelToken**。 **并发请求:** 使用 `Promise.all` 同时发起多个请求,全部成功返回结果数组,任一失败则整体失败。`axios.all` 已废弃(v0.27+),直接用 `Promise.all`。需要逐个处理结果用 `Promise.allSettled`,无论成功失败都返回每个请求的状态。 ```js // 并发请求 const [users, posts] = await Promise.all([ axios.get('/api/users'), axios.get('/api/posts') ]); // 容错并发 const results = await Promise.allSettled([ axios.get('/api/a'), axios.get('/api/b') ]); results.forEach(r => { if (r.status === 'fulfilled') handleData(r.value.data); }); ``` **取消请求:** 新方案用 `AbortController`(v0.22+推荐),旧方案 `CancelToken` 已废弃。AbortController 是浏览器原生 API,与 fetch 通用。 ```js // AbortController 取消 const controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }); // 取消 controller.abort(); // 封装自动取消:同一接口新请求自动取消旧请求 const pending = new Map(); function fetchWithCancel(url) { pending.get(url)?.abort(); const ctrl = new AbortController(); pending.set(url, ctrl); return axios.get(url, { signal: ctrl.signal }).finally(() => pending.delete(url)); } ``` > **追问:** > 1. Promise.all 和 Promise.allSettled 在错误处理上的区别是什么? > 2. 如何控制并发数量(如最多同时 3 个请求)? > 3. 取消请求后 axios 抛出的是什么错误?如何区分取消和真正的请求失败? > 4. 路由切换时如何批量取消未完成的请求? > 5. CancelToken 为什么被废弃?它和 AbortController 的实现原理有什么不同?
服务端5月29日 22:54
axios 文件上传下载怎么做?进度监控和 CSRF 防护如何实现?axios 的高级特性主要有 **文件上传下载、进度监控、CSRF 防护、实例封装**。 **文件上传:** 使用 FormData 构建请求体,设置 `Content-Type: multipart/form-data`(axios 自动识别 FormData 并设置)。分片上传需手动将文件切片(Blob.slice),逐片上传并在后端合并。上传示例:`const form = new FormData(); form.append('file', fileBlob); axios.post('/upload', form)`。 **文件下载:** 配置 `responseType: 'blob'` 获取二进制数据,通过 `URL.createObjectURL(blob)` 创建临时链接,触发 `<a>` 标签下载。大文件下载注意内存,可用流式处理(Node 环境)。 **进度监控:** 上传用 `onUploadProgress`,下载用 `onDownloadProgress`,回调参数包含 `loaded` 和 `total`,计算百分比:`Math.round(loaded / total * 100)`。底层基于 XMLHttpRequest 的 progress 事件,Node 环境不支持。 **CSRF 防护:** 配置 `xsrfCookieName` 和 `xsrfHeaderName`,axios 自动从 Cookie 读取 XSRF-Token 并写入请求头。后端需在登录时设置 `Set-Cookie: XSRF-TOKEN=xxx; Path=/`。 **实例封装:** `axios.create()` 创建独立实例,拥有自己的拦截器、默认配置和适配器,适合多 API 服务的项目隔离配置。 > **追问:** > 1. 分片上传如何实现断点续传?需要后端配合什么接口? > 2. onUploadProgress 在 Node 环境下为什么不生效?有替代方案吗? > 3. 大文件下载如何避免浏览器内存溢出? > 4. axios.create 创建的实例和全局 axios 对象的拦截器是共享的吗? > 5. XSRF-Token 的 Cookie 为什么不能设 HttpOnly?这和 XSS 风险如何权衡?
服务端5月29日 22:54
axios 和 fetch 有什么区别?什么时候该用 axios 而非 fetch?axios 和 fetch 的核心区别在 **错误处理、拦截器、请求取消、数据转换、兼容性** 五个方面。 **错误处理:** fetch 只有网络故障才 reject,HTTP 4xx/5xx 仍走 resolve,需手动检查 `response.ok`;axios 自动将 4xx/5xx 转为 reject,错误处理更符合直觉。 **拦截器:** axios 内建请求/响应拦截器,统一添加 Token、处理错误码;fetch 无拦截器,需手动包装或用第三方库。 **请求取消:** axios 支持 CancelToken 和 AbortController;fetch 只支持 AbortController。axios 的超时配置(timeout)是内建的,fetch 需自己用 AbortController + setTimeout 实现。 **数据转换:** axios 自动 JSON 转换(请求时 stringify、响应时 parse);fetch 需手动调用 `response.json()`。axios 自动处理 URL 编码和 FormData。 **兼容性:** fetch 是浏览器原生 API,无依赖;axios 约 13KB gzipped。fetch 在旧浏览器需 polyfill(如 whatwg-fetch)。 **选 axios 的场景:** 需要拦截器、自动错误处理、超时控制、请求/响应转换、取消请求。**选 fetch 的场景:** 追求零依赖、简单请求、Service Worker 中使用、项目已在用 SWR/React Query 等上层库。 > **追问:** > 1. fetch 的 `credentials: 'include'` 和 axios 的 `withCredentials: true` 行为是否一致? > 2. fetch 如何实现和 axios 拦截器类似的功能? > 3. 在 Service Worker 中为什么推荐用 fetch 而非 axios? > 4. axios 的响应拦截器能拿到原始的 Response 对象吗? > 5. React Query 或 SWR 底层用的是 axios 还是 fetch?能切换吗?
服务端5月29日 22:54
axios 性能优化有哪些技巧?如何减少冗余请求?axios 性能优化围绕 **请求去重、缓存、并发控制、体积优化** 四个维度。 **请求去重:** 对同一接口的并发请求做合并,避免重复发送。实现方式:在请求拦截器中用 URL+参数生成 key,维护一个 Map 记录正在进行的请求,相同 key 的请求复用同一个 Promise,响应后删除 key。也叫"请求锁"或"请求合并"。 **请求缓存:** 对不常变的数据(配置信息、字典表)做本地缓存。在响应拦截器中按 URL+参数缓存响应,设置 TTL 过期。也可用 HTTP 缓存头(Cache-Control、ETag)配合浏览器缓存,axios 的 `adapter` 可实现自定义缓存适配器。 **并发控制:** 使用 `Promise.all` 并发请求提升加载速度;对大量并发用并发池控制(如 p-limit),避免浏览器同域 6 连接限制导致的排队。批量接口优先用后端聚合 API,减少请求次数。 **体积优化:** 开启 gzip 压缩(服务端配置);请求参数精简,只传必要字段;响应数据按需获取(分页、字段过滤);大文件上传用分片上传减少超时风险。 > **追问:** > 1. 请求去重的 Map 在请求失败时如何清理?避免后续请求被阻塞? > 2. 自定义缓存 adapter 如何处理 POST 请求的缓存策略? > 3. 浏览器同域并发连接限制对 axios 有什么影响?如何绕过? > 4. 如何实现 axios 请求的优先级队列? > 5. 分片上传的断点续传如何在 axios 中实现?
服务端5月29日 22:54
axios 存在哪些安全风险?如何防范 XSS 和 CSRF 攻击?axios 的安全风险集中在 **CSRF、XSS、敏感数据泄露、SSRF** 四个方面。 **CSRF 防护:** axios 内置 CSRF 防护,通过 `xsrfCookieName` 和 `xsrfHeaderName` 配置自动从 Cookie 读取 Token 并附加到请求头。后端需设置 `Set-Cookie: XSRF-TOKEN=xxx`,前端配置 `axios.defaults.xsrfCookieName = 'XSRF-TOKEN'`。对于 SameSite Cookie 策略,建议后端设置 `SameSite=Strict` 或 `Lax` 作为双重保障。 **XSS 防护:** axios 本身不执行 HTML,但返回数据若直接插入 DOM(如 v-html、dangerouslySetInnerHTML)会导致 XSS。必须对响应数据做转义,或使用 DOMPurify 净化。另外避免在 URL 参数中拼接用户输入,防止反射型 XSS。 **敏感数据泄露:** Token 不要存 localStorage(XSS 可读取),优先存 HttpOnly Cookie;请求拦截器中不要把 Token 打印到日志;响应拦截器中敏感字段(密码、密钥)应在日志中脱敏。 **SSRF 防护:** 服务端使用 axios 时,若 URL 来自用户输入,需校验目标地址,禁止请求内网 IP(127.0.0.1、10.x.x.x、192.168.x.x),使用白名单域名策略。 > **追问:** > 1. axios 的 XSRF 防护机制在前后端分离架构下如何实现? > 2. SameSite Cookie 的 Strict 和 Lax 模式对 CSRF 防护有什么影响? > 3. HttpOnly Cookie 方案在跨域场景下如何配置 CORS? > 4. 如何对 axios 响应数据做自动化脱敏? > 5. SSRF 攻击中如何绕过 IP 黑名单?白名单方案怎么设计?
服务端5月29日 22:54
axios 代码怎么做单元测试?Mock 请求的常用方案有哪些?测试 axios 代码的核心是 **隔离网络请求**,常用方案有三种:axios-mock-adapter、jest.mock + msw。 **axios-mock-adapter:** 直接拦截 axios 实例的请求,按 URL 和方法注册 mock 响应。适合纯 axios 项目,API 直观。示例:`mock.onGet('/api/user').reply(200, { name: 'test' })`。可模拟超时、网络错误、指定状态码。缺点是绑定了 axios 实例,切换请求库需重写 mock。 **jest.mock('axios'):** 直接 mock axios 模块,控制 `axios.get/post` 等方法的返回值。适合快速编写测试,`jest.mock('axios')` 后用 `axios.get.mockResolvedValue()` 设定返回。优点是零依赖,缺点是绕过了拦截器逻辑,无法测试中间件行为。 **MSW(Mock Service Worker):** 在 Service Worker 层拦截请求,不依赖任何 HTTP 库。最接近真实网络行为,可共享给集成测试和 E2E。通过 handler 匹配请求并返回 mock 数据。推荐用于新项目。 **选型建议:** 项目只用 axios → axios-mock-adapter;需要快速单元测试 → jest.mock;多请求库或需要更真实模拟 → MSW。 > **追问:** > 1. jest.mock 和 jest.spyOn 在 mock axios 时有什么区别? > 2. 如何测试请求拦截器中的 Token 注入逻辑? > 3. MSW 的 Service Worker 在 Node 环境下如何工作? > 4. 如何模拟 axios 的网络超时和 5xx 错误场景? > 5. axios-mock-adapter 能否模拟请求进度(onUploadProgress)?
服务端5月29日 22:54
axios 底层是怎么实现的?核心架构和请求流程是怎样的?axios 本质是 **请求/响应拦截器管道 + 适配器模式**,核心流程为:配置合并 → 请求拦截器 → 分发请求 → 响应拦截器 → 返回结果。 **适配器模式:** axios 不直接发请求,而是通过 adapter 抽象层。浏览器环境用 `xhr.js`(基于 XMLHttpRequest),Node 环境用 `http.js`(基于 Node 的 http/https 模块)。通过 `adapter` 配置项可自定义适配器,这是 axios 跨平台的关键。 **拦截器机制:** 维护两个链式数组 `request interceptors` 和 `response interceptors`,每个拦截器有 fulfilled 和 rejected 两个回调。请求时将配置、拦截器和 adapter 按 Promise 链顺序串联执行,形成 `[req1 → req2 → ... → adapter → ... → res2 → res1]` 的管道。 **配置合并策略:** 三层配置合并——默认配置(defaults)→ 实例配置(instance.defaults)→ 请求配置(请求参数),通过 `mergeConfig` 按策略合并,headers 单独处理。 **请求流程:** `axios.request()` 是统一入口,其他方法(get/post 等)最终都调用 request。内部创建 Promise 链,将拦截器成对插入链的两端,adapter 在链中间执行实际请求,支持取消(CancelToken)和超时控制。 > **追问:** > 1. 拦截器的执行顺序是什么?请求拦截器和响应拦截器分别是正序还是逆序? > 2. 如何自定义 adapter 实现 Mock 数据或缓存层? > 3. CancelToken 基于什么原理实现的?为什么新增 AbortController 方案? > 4. mergeConfig 对不同类型的配置项(如 headers、transformRequest)采用了什么合并策略? > 5. axios 的 Promise 链如果中间某个拦截器抛异常,后续拦截器还能执行吗?
服务端5月29日 01:38
axios 和 fetch 有什么区别?什么时候该用 axios?axios 是基于 Promise 的 HTTP 客户端,相比原生 fetch 的核心优势在于:请求/响应拦截器(统一添加 token、错误处理)、自动 JSON 转换(fetch 需手动 .json())、请求超时配置(fetch 需封装 AbortController+setTimeout)、上传进度监控、XSRF 防护,以及 4xx/5xx 自动 reject(fetch 只在网络故障时才 reject)。但 fetch 是浏览器原生 API,零体积开销,且正逐步补齐能力(AbortController 已支持取消)。 ## 追问 **axios 的拦截器机制是怎么实现的?** 内部维护请求和响应两个拦截器数组(数组链),发送请求时按序执行请求拦截器,收到响应后按序执行响应拦截器,本质是 Promise 链式调用。 **fetch 如何实现请求超时?** 用 AbortController 创建 signal,配合 setTimeout 调用 controller.abort(),fetch 接收 signal 参数,超时后抛出 AbortError。axios 直接配置 timeout 字段即可。 **axios 在浏览器和 Node.js 端分别用什么发送请求?** 浏览器端基于 XMLHttpRequest,Node.js 端基于 http/https 模块,通过适配器模式统一 API。fetch 在 Node 18+ 才原生支持。 **axios 如何实现 XSRF 防护?** 读取指定 cookie(默认 XSRF-TOKEN)的值,自动写入请求头(默认 X-XSRF-TOKEN),配合后端双重 cookie 验证机制防跨站请求伪造。 ## 写段代码 ```javascript const instance = axios.create({ baseURL: '/api', timeout: 5000, }); instance.interceptors.request.use(cfg => { cfg.headers.Authorization = `Bearer ${token}`; return cfg; }); instance.interceptors.response.use( res => res.data, err => { if (err.response?.status === 401) redirectToLogin(); } ); ```