✨宗派背景
[!Tip] 传统请求
在 Ajax(2005 年)出现之前,传统浏览器实现 HTTP 请求的方式都是同步的,且必然伴随整个页面的刷新或跳转,核心依赖于浏览器的原生 HTML 解析和请求机制。主要方式有以下几种:
1. 表单提交(<form>
标签)
这是传统浏览器处理用户输入并与服务器交互的最主要方式。
原理:通过 <form>
标签定义请求参数,用户点击提交按钮后,浏览器会将表单数据按照指定的 HTTP 方法(GET/POST)打包,发送到 action
属性指定的服务器 URL,然后加载服务器返回的全新 HTML 页面,替换当前页面。
示例代码:
<!-- 传统表单提交 -->
<form action="/submit" method="POST">
<input type="text" name="username" placeholder="用户名">
<input type="password" name="password" placeholder="密码">
<button type="submit">登录</button>
</form>
特点:
- 同步阻塞:提交后浏览器会 “冻结”,等待服务器响应,期间用户无法操作页面。
- 全页面刷新:服务器返回的新 HTML 会完全替换当前页面的 DOM,导致页面闪烁。
- 数据传输:GET 方法将数据拼在 URL 后(
?username=xxx&password=xxx
),POST 方法将数据放在请求体中。
2. 锚点链接(<a>
标签)
通过超链接发起 HTTP GET 请求,用于页面跳转或资源访问。
原理:<a>
标签的 href
属性指定目标 URL,用户点击后,浏览器向该 URL 发送 GET 请求,服务器返回新页面(或资源),浏览器加载并替换当前页面。
示例代码:
<!-- 超链接发起GET请求 -->
<a href="/user/list">查看用户列表</a>
<a href="/static/image.jpg">查看图片</a>
特点:
- 仅支持 GET 方法,数据通过 URL 参数传递(如
/user?page=1
)。 - 同步跳转:点击后立即触发页面跳转,当前页面状态(如输入框内容)会丢失。
3. 资源标签的自动请求(<img>
、<script>
、<link>
等)
浏览器在解析 HTML 时,会自动对带有 src
或 href
属性的资源标签发起 HTTP GET 请求,用于加载图片、脚本、样式表等。
示例代码:
<!-- 浏览器自动发起请求加载资源 -->
<img src="/static/logo.png" alt="网站logo"> <!-- 加载图片 -->
<script src="/js/util.js"></script> <!-- 加载JavaScript -->
<link rel="stylesheet" href="/css/style.css"> <!-- 加载CSS -->
特点:
- 被动触发:由浏览器解析 HTML 时自动发起,无需用户操作。
- 同步阻塞(部分):例如
<script>
标签加载脚本时,会阻塞后续 HTML 解析和渲染(直到脚本下载并执行完成),避免 JS 操作未解析的 DOM。 - 仅支持 GET 方法,且无法直接获取响应数据(资源被浏览器直接用于渲染或执行)。
4. 刷新按钮或地址栏输入 URL
用户点击浏览器的 “刷新” 按钮,或在地址栏直接输入 URL 并回车,本质上是手动触发浏览器向指定 URL 发送 GET 请求,获取完整 HTML 页面并刷新。
5.传统浏览器实现ResultFul接口
在传统浏览器(Ajax 出现之前,主要依赖 HTML 原生特性)中:
- POST 请求并非只能通过
<form>
发送,但<form>
是最主要且标准化的方式; - DELETE、PUT 等 HTTP 方法无法通过原生 HTML 直接发送,因为传统浏览器的
<form>
标签仅支持GET
和POST
两种方法(method
属性只能指定这两个值)。
1. 传统浏览器中发送 POST 请求的方式
除了 <form>
标签,理论上还有一些 “非标准” 方式,但实际开发中几乎不用:
<iframe>
配合表单提交:通过隐藏的<iframe>
作为表单的target
,实现 “伪异步” POST 请求(但本质仍是同步提交,只是页面不刷新),但这仍依赖<form>
标签的 POST 能力。- 早期插件 / ActiveX:如 IE 中的 ActiveX 对象(如
XMLHTTP
早期版本),但这属于浏览器扩展,并非跨浏览器的原生 HTML 特性,且在 Ajax 普及前未被广泛使用。
因此,<form>
标签是传统浏览器中发送 POST 请求的标准且唯一可靠的方式。
2. 传统浏览器中如何 “模拟” DELETE、PUT 请求?
由于传统浏览器不支持直接发送 DELETE、PUT 等方法,实际开发中通常通过 “POST 伪装” 实现,核心思路是:
- 用
<form>
发送 POST 请求; - 在请求中加入一个特殊参数(如
_method
),值为DELETE
或PUT
; - 服务器端接收 POST 请求后,检查该参数,将请求 “转换” 为对应的 DELETE 或 PUT 方法处理。
示例代码(前端):
<!-- 模拟 PUT 请求 -->
<form action="/user/123" method="POST">
<!-- 隐藏字段指定实际方法 -->
<input type="hidden" name="_method" value="PUT">
<input type="text" name="username" value="newName">
<button type="submit">更新用户</button>
</form>
<!-- 模拟 DELETE 请求 -->
<form action="/user/123" method="POST">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">删除用户</button>
</form>
服务器端处理(以 Nede.js(Express) 为例):
// 中间件解析 _method 参数,转换请求方法
app.use((req, res, next) => {
if (req.method === 'POST' && req.body._method) {
req.method = req.body._method.toUpperCase(); // 转换为 PUT/DELETE
delete req.body._method;
}
next();
});
// 处理 PUT 请求
app.put('/user/:id', (req, res) => {
// 实际处理更新逻辑
});
// 处理 DELETE 请求
app.delete('/user/:id', (req, res) => {
// 实际处理删除逻辑
});
这种方式本质上仍是 POST 请求,只是通过服务器端转换实现了对其他 HTTP 方法的支持,是传统浏览器时代实现 RESTful 接口的妥协方案。
总结
- 传统浏览器中,
<form>
是发送 POST 请求的标准方式,其他方式(如插件)不具备通用性; - DELETE、PUT 等方法无法通过原生 HTML 直接发送,需通过 “POST + 特殊参数” 的方式模拟,由服务器端转换处理;
- 直到 Ajax(XMLHttpRequest)出现后,浏览器才支持直接发送任意 HTTP 方法(包括 DELETE、PUT),无需依赖表单或参数转换。
传统方式的共同局限
- 必须刷新页面:所有请求的最终结果都是加载新的 HTML 页面,无法实现页面局部更新。
- 同步阻塞:请求过程中浏览器无法响应用户操作(如点击、输入),用户体验差。
- 数据冗余:每次请求都需要传输完整的 HTML 页面(即使只有一小部分内容变化),浪费带宽。
- 状态丢失:页面刷新后,当前页面的临时状态(如输入框内容、滚动位置)会被清空。
这些局限性正是 Ajax(基于 XMLHttpRequest)被提出的原因 —— 通过异步请求 + 局部 DOM 更新,解决传统同步请求的痛点,让网页交互更接近桌面应用的流畅体验。
🥸宗主Ajax
一、Ajax 的来由
[!Tip] 概念神
Ajax(Asynchronous JavaScript and XML,异步 JavaScript 和 XML)并非全新技术,而是 2005 年由 Jesse James Garrett 提出的一种技术整合方案。它将已有的技术(JavaScript、XMLHttpRequest、DOM、XML/JSON 等)结合起来,形成了一种新的网页交互模式,解决了传统网页 “每次更新都需刷新整个页面” 的问题。
二、Ajax 的作用
Ajax 的核心是异步通信,允许浏览器在不刷新整个页面的情况下,与服务器进行数据交互并局部更新页面内容。主要作用包括:
- 无刷新更新内容:如实时搜索建议、表单提交后即时反馈、动态加载列表等。
- 减少数据传输量:仅请求所需数据,而非整个页面,降低带宽消耗。
- 提升用户体验:避免页面闪烁和等待,交互更流畅。
- 异步处理:请求发送后不阻塞页面其他操作(如点击、滚动)。
三、Ajax 的优缺点
优点:
- 局部更新页面,减少用户等待时间,提升体验。
- 减少冗余数据传输,降低服务器和网络负载。
- 支持异步操作,不阻塞页面其他交互。
- 可与多种后端语言(Java、Python、PHP 等)配合使用。
缺点:
- 对搜索引擎不友好(爬虫难以抓取动态加载的内容)。
- 破坏浏览器 “前进 / 后退” 功能(需额外处理历史记录)。
- 异步逻辑复杂,早期回调嵌套易导致 “回调地狱”(现可通过 Promise/async/await 解决)。
- 受同源策略限制,跨域请求需额外配置(如 CORS)。
🗡️宗门圣器:XHR
[!Tip] 圣器
XMLHttpRequest(XHR),它是实现 Ajax 核心功能的关键技术。XMLHttpRequest 之所以能成为 Ajax 的核心,本质上是因为它提供了浏览器与服务器进行异步通信的能力,直接解决了传统网页 “必须刷新整个页面才能更新内容” 的痛点。
XMLHttpRequest 实现 Ajax 核心作用的关键特性
异步请求能力
XMLHttpRequest 允许在不阻塞页面渲染和用户操作的情况下发送 HTTP 请求。- 传统网页的表单提交或链接跳转是 “同步” 的:请求发送后,浏览器会等待服务器响应,期间页面无法交互,直到整个页面重新加载。
- 而 XHR 通过
open(method, url, async)
方法的第三个参数(async: true
)开启异步模式,请求发送后,浏览器可以继续处理其他操作(如用户点击、滚动),直到服务器返回数据后,通过回调函数(如onreadystatechange
)处理响应。
这种 “后台悄悄通信” 的特性,是 Ajax 实现 “无刷新更新” 的基础。
直接与服务器交换数据
XMLHttpRequest 可以直接向服务器发送 HTTP 请求(GET、POST 等),并接收服务器返回的数据(XML、JSON、文本等),无需通过整个页面的刷新。- 这意味着浏览器可以只请求 “需要更新的部分数据”(而非整个 HTML 页面),大幅减少数据传输量,提升效率。
- 例如:在搜索框输入时,通过 XHR 向服务器发送输入的关键词,服务器返回匹配的建议列表,浏览器用 JavaScript 动态更新下拉框,整个过程无需刷新页面。
支持局部页面更新
XHR 接收数据后,结合 JavaScript 和 DOM 操作,可以只更新页面中需要变化的部分,而非整个页面。- 传统网页刷新会导致整个 DOM 树重新构建,页面闪烁且用户体验差。
- 而 XHR 获取数据后,通过
document.getElementById()
等方法定位到具体 DOM 元素,用新数据修改其内容(如innerHTML
),实现 “局部刷新”。
状态监听与灵活控制
XHR 提供了完善的状态机制,允许开发者精确控制请求的生命周期:readyState
属性:表示请求的状态(0-4,4 表示请求完成)。status
属性:表示 HTTP 响应状态码(如 200 表示成功,404 表示未找到)。- 事件回调(如
onreadystatechange
、onload
、onerror
):可以在请求完成、成功、失败等不同阶段执行对应的逻辑(如显示加载动画、处理错误信息)。
总结
Ajax 的核心是 “异步通信 + 局部更新”,而 XMLHttpRequest 正是提供这种能力的技术载体:它让浏览器能在后台与服务器交换数据,同时不阻塞用户操作,最后通过 JavaScript 操作 DOM 实现页面局部更新。因此,XMLHttpRequest 是 Ajax 能够颠覆传统网页交互模式的核心技术基础。
🤩大师兄Axios
1. 底层依赖
[!Tip]
Axios 是一个 第三方库(需通过 npm 安装或 CDN 引入),底层在 浏览器环境 中依赖XMLHttpRequest
对象,在 Node.js 环境 中依赖http
/https
模块,因此支持全端使用。
它本质是对原生请求能力的 高级封装,核心目标是简化网络请求的配置、处理和扩展。
2. 核心工作流程
Axios 的基本使用如下,同样基于 Promise:
// Axios 基本使用
axios({
url: 'https://api.example.com/data',
method: 'GET'
})
.then(response => console.log('数据:', response.data))
.catch(error => console.error('错误:', error));
详细原理步骤:
- 1. 合并配置:Axios 接收用户传入的配置(
url
、method
、data
等),与默认配置(baseURL
、headers
等)合并,生成最终请求配置。 - 2. 执行请求拦截器:通过
axios.interceptors.request
注册的拦截器函数,可修改请求配置(如添加 token)、终止请求等。 - 3. 发起请求:
- 浏览器环境:创建
XMLHttpRequest
对象,根据配置设置请求方法、URL、请求头,发送请求体(data
)。 - Node.js 环境:使用
http
模块创建请求,处理参数序列化和发送。
- 浏览器环境:创建
- 4. 监听响应:
- 浏览器中通过
XMLHttpRequest.onreadystatechange
监听请求状态,当readyState === 4
时触发响应处理。 - 解析响应数据:自动将响应体转换为 JSON(如果响应头
Content-Type
为application/json
)。
- 浏览器中通过
- 5. 执行响应拦截器:通过
axios.interceptors.response
注册的拦截器函数,可处理响应数据(如统一解密)、拦截错误(如 401 跳转登录)。 - 6. 处理状态:
- 网络错误或 HTTP 状态码非 2xx 时,Promise 进入
reject
状态(无需手动判断)。 - 成功时返回的
response
对象包含data
(解析后的数据)、status
、headers
等。
- 网络错误或 HTTP 状态码非 2xx 时,Promise 进入
3. 核心特性
- 全端支持:浏览器(XMLHttpRequest)和 Node.js(http 模块)通用。
- 强大的拦截器:支持请求 / 响应拦截,可在请求发送前或响应处理前统一处理逻辑(如 token 管理、错误统一处理)。
- 自动数据转换:请求时自动将
data
转换为 JSON 字符串,响应时自动解析 JSON 数据。 - 超时控制:内置
timeout
配置,超过指定时间自动终止请求。 - 取消请求:通过
CancelToken
或AbortController
取消正在进行的请求。 - 请求并发:提供
axios.all()
和axios.spread()
处理并发请求。 - 浏览器兼容性:支持 IE8+(需引入
es6-promise
polyfill 处理 Promise)。
🤩宗门天骄Fetch
一、Fetch API 原理
1. 底层依赖
Fetch 是 浏览器原生的 API(ES6+ 引入),基于 JavaScript 的 Promise
设计,直接由浏览器引擎实现(无需额外引入库)。它的底层不依赖传统的 XMLHttpRequest
,而是使用了更现代的浏览器网络请求接口(可理解为浏览器内核级别的 HTTP 客户端实现)。
2. 核心工作流程
Fetch 的核心是通过 fetch()
函数发起请求,返回一个 Promise 对象,流程如下:
// Fetch 基本使用
fetch(url, options)
.then(response => {
// 处理响应(需手动解析响应体)
if (!response.ok) throw new Error('请求失败');
return response.json(); // 解析 JSON 响应
})
.then(data => console.log('数据:', data))
.catch(error => console.error('错误:', error));
详细原理步骤:
- 1. 配置请求参数:
fetch(url, options)
接收两个参数,url
为请求地址,options
为配置对象(包含method
、headers
、body
、mode
等)。 - 2. 发起请求:浏览器内核通过底层网络模块发送 HTTP 请求(支持 GET、POST、PUT、DELETE 等所有 HTTP 方法)。
- 3. 接收响应:请求完成后,返回一个
Response
对象(包含响应状态status
、响应头headers
、响应体body
等元数据)。 - 4. 解析响应体:
Response
对象的响应体是一个 可读流(ReadableStream),需通过json()
、text()
、blob()
等方法手动解析(这些方法返回新的 Promise,等待流解析完成)。 - 5. 处理状态:
- 只有 网络错误(如断网)时,Promise 才会进入
reject
状态; - HTTP 错误状态码(如 404、500)不会触发
reject
,需通过response.ok
(status
为 200-299 时为true
)手动判断。
- 只有 网络错误(如断网)时,Promise 才会进入
3. 核心特性
- 原生无依赖:浏览器内置,无需引入第三方库。
- 基于 Promise:支持
async/await
语法,解决回调地狱。 - 响应体流式处理:响应体以流的形式返回,适合处理大文件(如视频、大型 JSON)。
- 跨域控制:通过
options.mode
配置跨域策略(cors
、no-cors
、same-origin
)。 - 局限性:
- 无默认超时设置(需手动通过
AbortController
实现)。 - 无请求 / 响应拦截器(需手动封装)。
- 不支持自动转换 JSON 数据(需显式调用
response.json()
)。 - 浏览器兼容性:IE 完全不支持,需通过 polyfill 兼容旧浏览器。
- 无默认超时设置(需手动通过
🎆比武大会
对比维度 | axios | fetch |
---|---|---|
底层依赖 | 浏览器端基于 XMLHttpRequest ,Node 端基于 http 模块 |
浏览器原生 API,基于更现代的网络接口(非 XHR) |
API 设计 | 函数式 API,支持 axios(config) 或快捷方法(axios.get() ) |
全局 fetch(url, options) 函数,返回 Promise |
返回值 | 包装后的响应对象({ data, status, headers, ... } ) |
原生 Response 对象(需手动解析响应体) |
数据转换 | 自动转换: - 请求时自动将 data 转为 JSON - 响应时自动解析 JSON |
需手动解析: - 响应体通过 response.json() /text() 等方法解析(返回新 Promise) |
错误处理 | 主动捕获错误: - 网络错误触发 reject - HTTP 非 2xx 状态码(如 404)触发 reject |
被动捕获错误: - 仅网络错误触发 reject - HTTP 错误状态码(如 404)不触发 reject,需通过 response.ok 判断 |
拦截器 | 内置请求 / 响应拦截器(axios.interceptors ),可全局处理请求(如添加 token) |
无内置拦截器,需手动封装(如通过高阶函数) |
超时控制 | 内置 timeout 配置(如 { timeout: 3000 } ) |
无内置超时,需通过 AbortController + Promise.race 实现 |
取消请求 | 支持两种方式: - 旧版 CancelToken (已废弃) - 新版 AbortController (推荐) |
仅支持 AbortController (与 Web 标准一致) |
请求体格式 | 自动处理不同格式: - JSON 自动设置 Content-Type: application/json - FormData 自动设置 multipart/form-data |
需手动设置 Content-Type : - 如 JSON 需显式设置 headers: { 'Content-Type': 'application/json' } |
并发请求 | 提供 axios.all() 和 axios.spread() 处理并发 |
需通过 Promise.all() 手动处理 |
浏览器兼容性 | 支持 IE8+(需 es6-promise 兼容 Promise) |
不支持 IE,现代浏览器(Chrome 42+、Firefox 39+)支持 |
额外功能 | 支持 JSONP、请求重试、防御 XSRF 等 | 无额外功能,仅提供基础请求能力 |