create-react-app 中用 AJAX 请求获取数据:fetch、Promise 与 react-app-polyfill 浏览器兼容方案
React 本身并不规定具体的数据获取方式,create-react-app 生成的项目在实践中通常使用 axios 这类第三方库,或浏览器内置的 fetch() API 来发起 AJAX 请求。读完本文,你将掌握 fetch() 与 Promise、async / await 的标准用法,并了解在 create-react-app 项目中为旧浏览器(尤其是 Internet Explorer)补齐运行时能力的完整方案——即仓库内置的 react-app-polyfill 包的四个入口点及其源码实现。
两种主流的数据获取方式
React 不预设数据获取方案,社区常见的选择有两种:
- axios 等第三方库:在
package.json中安装依赖后在组件中import使用,API 更贴近 XMLHttpRequest,内置了请求/响应拦截器、自动 JSON 转换等能力; - 浏览器原生
fetch()API:无需安装任何依赖,由全局fetch函数直接发起请求。
无论选择哪种方式,二者的底层都依赖 Promise 机制。
fetch() API:返回 Promise 的请求机制
全局 fetch 函数接收一个 URL 作为输入,返回一个 Promise,该 Promise 最终解析为一个 Response 对象。基本用法如下:
// 发起 GET 请求
fetch('https://api.example.com/items')
.then((response) => {
// Response 对象需要先转换为 JSON
return response.json();
})
.then((data) => {
// 这里拿到解析后的数据
console.log(data);
})
.catch((error) => {
console.error('请求失败', error);
});
需要特别注意的是,Response 对象本身只是 HTTP 响应(包含 status、headers 等元信息),业务数据还需要通过 response.json() 等方法进一步解析。由于 fetch 返回的 Promise 只在网络层失败(如断网、DNS 解析失败)时 reject,HTTP 4xx/5xx 状态码并不会自动进入 catch 分支,通常需要自行检查 response.ok:
fetch('/api/notes')
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP 错误状态:${response.status}`);
}
return response.json();
})
.then((notes) => {
console.log(notes);
});
用 async / await 简化异步逻辑
Promise 是异步操作的最终结果的抽象。axios 与 fetch() 底层都使用 Promise,而 async / await 语法可以进一步减少回调嵌套,使异步代码更接近同步写法:
async function loadUser() {
try {
const response = await fetch('/api/user/1');
if (!response.ok) {
throw new Error(`HTTP 错误状态:${response.status}`);
}
const user = await response.json();
return user;
} catch (error) {
console.error('请求失败', error);
}
}
从 create-react-app 的 kitchensink 集成测试中也能看到这一点:async / await 是被项目作为一等公民支持的语法特性,对应测试文件为 AsyncAwait.js 及其测试用例 AsyncAwait.test.js,说明该语法在构建管线(Babel 转译)层面是被完整支持的。
关键前提:目标浏览器必须支持 fetch 和 Promise
在使用上述 API 前,必须确认目标受众的浏览器环境中 fetch() API 和 Promise 是可用的。对于现代浏览器,二者均已原生支持;但对于 Internet Explorer 等旧浏览器,则必须手动引入 polyfill。create-react-app 针对这一场景提供了仓库内置的 react-app-polyfill 包,这一点在官方浏览器支持文档 supported-browsers-features.md 中也有明确说明:默认生成的项目只支持所有现代浏览器,IE 9/10/11 支持需要额外引入 polyfill,且项目默认不包含任何 polyfill。
这一点可以直接从模板源码得到印证:默认 JS 模板的入口文件 index.js 中没有任何 polyfill 导入,第一行就是 import React from 'react';TypeScript 模板的 index.tsx 同样如此。
react-app-polyfill 的四个入口点
react-app-polyfill 包(见 package.json)提供四个面向不同目标的入口文件,这也是 files 字段中声明的全部产物:
| 入口 | 适用场景 | 提供的能力 |
|---|---|---|
react-app-polyfill/ie11 |
支持 IE 11 | Promise、window.fetch、Object.assign、Symbol、Array.from |
react-app-polyfill/ie9 |
支持 IE 9(自动覆盖 IE 10/11) | 在 ie11 基础上追加 Map、Set、requestAnimationFrame |
react-app-polyfill/stable |
补齐目标浏览器缺失的稳定语言特性 | core-js/stable 全套稳定 polyfill + regenerator-runtime |
react-app-polyfill/jsdom |
测试环境(Jest 的 jsdom) | 仅 fetch polyfill |
使用前先安装:
npm install react-app-polyfill
或:
yarn add react-app-polyfill
如果只需要支持 IE 11,在 src/index.js 的第一行导入:
// This must be the first line in src/index.js
import 'react-app-polyfill/ie11';
// ...
如果需要支持 IE 9,则改为:
// This must be the first line in src/index.js
import 'react-app-polyfill/ie9';
// ...
由于 ie9 入口内部会先 require('./ie11')(见 ie9.js 第 9 行),所以导入 ie9 会自动包含 ie11 的全部能力。
源码解析:ie11 入口补齐了哪些能力
阅读 ie11.js 的实现,可以看到它对 CRA 项目运行所必需的五个语言特性逐项补齐:
if (typeof Promise === 'undefined') {
// Rejection tracking prevents a common issue where React gets into an
// inconsistent state due to an error, but it gets swallowed by a Promise,
// and the user has no idea what causes React's erratic future behavior.
require('promise/lib/rejection-tracking').enable();
self.Promise = require('promise/lib/es6-extensions.js');
}
这里有两个值得注意的细节:
- 按需替换:只有当原生
Promise不存在时才挂载 polyfill,避免覆盖浏览器原生实现; - 开启 rejection tracking:注释明确说明,这是为了防止 Promise 吞掉错误导致 React 陷入不一致状态——被吞掉的 rejection 会让用户无法解释 React 后续的异常行为。对于数据获取场景,这意味着在 IE 环境下未处理的
fetch().catch()丢失仍然能通过全局机制被追踪到。
// Make sure we're in a Browser-like environment before importing polyfills
// This prevents `fetch()` from being imported in a Node test environment
if (typeof window !== 'undefined') {
// fetch() polyfill for making API calls.
require('whatwg-fetch');
}
typeof window !== 'undefined' 这层守卫是一个关键设计:防止在 Node 测试环境(Jest)中引入 whatwg-fetch。Jest 默认运行在 jsdom 环境,若不加守卫,fetch polyfill 会污染 Node 全局对象。仓库变更历史中也有对应的修复记录("Don't polyfill fetch for Node"),说明这是真实踩坑后沉淀下来的防御逻辑。
其余部分则补齐数据获取和组件开发中高频使用的特性:
Object.assign = require('object-assign')—— React 开发中常用(也是 Object Spread 语法{ ...a, ...b }转译后的运行时依赖);core-js/features/symbol—— 支撑for...of等依赖 Symbol 的语法;core-js/features/array/from—— 支撑数组展开[...arr]以及可迭代对象展开(...Set、...Map)。
而 ie9.js 在此之上追加了 core-js/features/map、core-js/features/set 以及 raf(requestAnimationFrame polyfill),源码注释说明这些是 React 16+ 的运行时依赖。
stable 入口:结合 browserslist 补齐其余稳定特性
除了 IE 场景,stable 入口可以补齐目标浏览器缺失的其他稳定语言特性。其实现 stable.js 非常简洁:
// Polyfill stable language features.
// It's recommended to use @babel/preset-env and browserslist
// to only include the polyfills necessary for the target browsers.
require('core-js/stable');
require('regenerator-runtime/runtime');
源码注释指出推荐配合 Babel 的 @babel/preset-env 与 browserslist 配置,只为目标浏览器包含真正需要的 polyfill——在 create-react-app 中使用 stable 入口时,构建管线会自动依据项目 package.json 中定义的 browserslist 做按需裁剪。regenerator-runtime 则是 async / await 和 Generator 转译后的运行时支撑,这也是为什么数据获取代码使用 async / await 时,在旧浏览器上同样依赖 polyfill 的原因。
如果同时需要支持 IE 9/11 和更广泛的稳定特性,按官方说明应同时导入两个入口:
// For IE9:
import 'react-app-polyfill/ie9';
import 'react-app-polyfill/stable';
// For IE11:
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
测试环境:jsdom 入口
react-app-polyfill 还提供 jsdom.js 入口,其全部内容就是带 window 守卫的 whatwg-fetch 引入。它专为 Jest 的 jsdom 测试环境设计:如果组件测试中需要断言 fetch 调用或模拟网络响应,可以导入该入口让 fetch 在 jsdom 中可用(生产代码通常直接 mock fetch 即可,无需引入 polyfill)。
polyfill 依赖一览
从 package.json 可以看到各入口背后的实际依赖及其版本约束:
| 依赖 | 版本约束 | 对应能力 |
|---|---|---|
promise |
^8.1.0 |
IE 环境的 Promise 实现 |
whatwg-fetch |
^3.6.2 |
window.fetch 的 WHATWG 标准实现 |
object-assign |
^4.1.1 |
Object.assign |
core-js |
^3.19.2 |
Symbol、Array.from、Map、Set 等语言特性 |
raf |
^3.4.1 |
requestAnimationFrame(仅 ie9 入口) |
regenerator-runtime |
^0.13.9 |
async/await 与 Generator 运行时(仅 stable 入口) |
实践建议
- 默认面向现代浏览器:create-react-app 生成的项目默认不内置任何 polyfill,
fetch与 Promise 在现代浏览器中开箱即用,优先使用原生fetch()+async / await的写法,避免不必要的包体积开销; - 需要兼容 IE 时再引入:在
src/index.js第一行导入react-app-polyfill/ie11(或ie9),并视需要追加stable入口;导入顺序必须是第一行,保证 polyfill 在任何业务模块执行前生效; - 注意错误处理差异:
fetch不会对 HTTP 错误状态码 reject,response.ok检查要写进业务代码;而在 IE polyfill 环境下,未捕获的 Promise rejection 会因 rejection tracking 被暴露出来,这是排查"React 状态异常"问题的重要线索; - 测试环境隔离:Node/Jest 环境中不要手动引入 IE 类 polyfill,
react-app-polyfill的window守卫已自动避免这一风险。
对于如何在 React 组件中组织数据获取逻辑(加载状态、错误状态、请求时机等),React 官方文档中有专门的 AJAX FAQ 条目可供参考。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00