11. 数据获取 - fetch 与 axios概述数据获取是现代 React 应用的核心需求。fetch是浏览器原生 APIaxios是第三方 HTTP 客户端库。两者都能发送 HTTP 请求但在功能、语法和易用性上有所不同。维度内容What使用 fetch API 或 axios 库从服务器获取数据Why从后端 API 获取数据动态更新 UIWhen组件需要从服务器加载数据时WhereuseEffect 钩子中、事件处理函数中Who需要与服务端交互的开发者Howfetch(url).then(res res.json())或axios.get(url)1. fetch vs axios 对比1.1 快速对比表特性fetchaxios浏览器支持原生需要 polyfill 支持 IE所有浏览器包括 IE语法基于 Promise基于 PromiseJSON 处理需要手动.json()自动转换请求拦截不支持原生支持响应拦截不支持原生支持取消请求使用 AbortController支持 CancelToken超时设置不支持原生支持上传进度需要手动实现支持体积0KB原生~13KB压缩后错误处理只捕获网络错误捕获 HTTP 错误状态码1.2 基础语法对比// fetch 版本 fetch(https://api.example.com/users) .then(response { if (!response.ok) { throw new Error(HTTP error response.status); } return response.json(); }) .then(data console.log(data)) .catch(error console.error(error)); // axios 版本 axios.get(https://api.example.com/users) .then(response console.log(response.data)) .catch(error console.error(error));2. fetch API 详解2.1 基础 GET 请求import { useState, useEffect } from react; function FetchUsers() { const [users, setUsers] useState([]); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { fetch(https://jsonplaceholder.typicode.com/users) .then(response { if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } return response.json(); }) .then(data { setUsers(data); setLoading(false); }) .catch(err { setError(err.message); setLoading(false); }); }, []); if (loading) return div加载中.../div; if (error) return div错误: {error}/div; return ( ul {users.map(user ( li key{user.id}{user.name}/li ))} /ul ); }2.2 POST 请求function CreatePost() { const [title, setTitle] useState(); const [body, setBody] useState(); const [status, setStatus] useState(idle); const handleSubmit async (e) { e.preventDefault(); setStatus(loading); try { const response await fetch(https://jsonplaceholder.typicode.com/posts, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ title, body, userId: 1, }), }); if (!response.ok) { throw new Error(创建失败); } const data await response.json(); console.log(创建成功:, data); setStatus(success); setTitle(); setBody(); } catch (error) { console.error(错误:, error); setStatus(error); } }; return ( form onSubmit{handleSubmit} input typetext value{title} onChange{(e) setTitle(e.target.value)} placeholder标题 / textarea value{body} onChange{(e) setBody(e.target.value)} placeholder内容 / button typesubmit disabled{status loading} {status loading ? 提交中... : 提交} /button /form ); }2.3 PUT 和 DELETE 请求// PUT 更新 async function updateUser(id, userData) { const response await fetch(https://api.example.com/users/${id}, { method: PUT, headers: { Content-Type: application/json, }, body: JSON.stringify(userData), }); if (!response.ok) { throw new Error(更新失败); } return response.json(); } // DELETE 删除 async function deleteUser(id) { const response await fetch(https://api.example.com/users/${id}, { method: DELETE, }); if (!response.ok) { throw new Error(删除失败); } return true; }2.4 带参数的 GET 请求// 查询参数 function SearchUsers({ query }) { const [results, setResults] useState([]); useEffect(() { const url new URL(https://api.example.com/users); url.searchParams.append(q, query); url.searchParams.append(limit, 10); fetch(url) .then(res res.json()) .then(data setResults(data)); }, [query]); return UserList users{results} /; }2.5 请求取消AbortControllerfunction CancelableFetch() { const [data, setData] useState(null); useEffect(() { const abortController new AbortController(); fetch(https://api.example.com/slow-endpoint, { signal: abortController.signal, }) .then(res res.json()) .then(data setData(data)) .catch(err { if (err.name AbortError) { console.log(请求已取消); } else { console.error(错误:, err); } }); // 组件卸载时取消请求 return () abortController.abort(); }, []); return div{JSON.stringify(data)}/div; }2.6 超时处理手动实现async function fetchWithTimeout(url, options {}, timeout 5000) { const controller new AbortController(); const { signal } controller; const timeoutId setTimeout(() controller.abort(), timeout); try { const response await fetch(url, { ...options, signal }); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); if (error.name AbortError) { throw new Error(请求超时); } throw error; } } // 使用 fetchWithTimeout(https://api.example.com/data, {}, 3000) .then(res res.json()) .then(data console.log(data)) .catch(err console.error(err.message));3. axios 详解3.1 安装与配置npminstallaxiosimport axios from axios; // 全局配置 axios.defaults.baseURL https://api.example.com; axios.defaults.timeout 5000; axios.defaults.headers.common[Authorization] Bearer token; axios.defaults.headers.post[Content-Type] application/json;3.2 基础 GET 请求import { useState, useEffect } from react; import axios from axios; function AxiosUsers() { const [users, setUsers] useState([]); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { axios.get(https://jsonplaceholder.typicode.com/users) .then(response { setUsers(response.data); setLoading(false); }) .catch(err { setError(err.message); setLoading(false); }); }, []); if (loading) return div加载中.../div; if (error) return div错误: {error}/div; return ( ul {users.map(user ( li key{user.id}{user.name}/li ))} /ul ); }3.3 POST 请求async function createPost(postData) { try { const response await axios.post( https://jsonplaceholder.typicode.com/posts, postData, { headers: { Content-Type: application/json, }, } ); console.log(创建成功:, response.data); return response.data; } catch (error) { console.error(创建失败:, error.response?.data || error.message); throw error; } } // 使用 createPost({ title: My Post, body: This is the content, userId: 1 });3.4 并发请求async function fetchMultipleData() { try { const [usersResponse, postsResponse, commentsResponse] await Promise.all([ axios.get(https://api.example.com/users), axios.get(https://api.example.com/posts), axios.get(https://api.example.com/comments), ]); return { users: usersResponse.data, posts: postsResponse.data, comments: commentsResponse.data, }; } catch (error) { console.error(并发请求失败:, error); throw error; } }3.5 请求拦截器// 请求拦截器 axios.interceptors.request.use( config { // 在请求发送前做些什么 console.log(请求发送:, config.url); // 添加 token const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }, error { return Promise.reject(error); } ); // 响应拦截器 axios.interceptors.response.use( response { // 对响应数据做点什么 console.log(响应接收:, response.config.url); return response; }, error { // 对响应错误做点什么 if (error.response?.status 401) { // 未授权跳转到登录页 window.location.href /login; } return Promise.reject(error); } );3.6 创建实例// 创建多个实例不同配置 const apiClient axios.create({ baseURL: https://api.example.com, timeout: 5000, headers: { Content-Type: application/json, }, }); const uploadClient axios.create({ baseURL: https://upload.example.com, timeout: 30000, headers: { Content-Type: multipart/form-data, }, }); // 使用实例 apiClient.get(/users).then(res console.log(res.data)); uploadClient.post(/image, formData).then(res console.log(res.data));3.7 取消请求import { CancelToken } from axios; function CancelableAxios() { useEffect(() { const source CancelToken.source(); axios.get(https://api.example.com/slow-endpoint, { cancelToken: source.token, }) .then(response console.log(response.data)) .catch(err { if (axios.isCancel(err)) { console.log(请求已取消:, err.message); } else { console.error(错误:, err); } }); return () source.cancel(组件卸载取消请求); }, []); }3.8 上传进度function UploadFile() { const [progress, setProgress] useState(0); const handleUpload async (file) { const formData new FormData(); formData.append(file, file); try { const response await axios.post(/upload, formData, { headers: { Content-Type: multipart/form-data, }, onUploadProgress: (progressEvent) { const percentCompleted Math.round( (progressEvent.loaded * 100) / progressEvent.total ); setProgress(percentCompleted); }, }); console.log(上传成功:, response.data); } catch (error) { console.error(上传失败:, error); } }; return ( div input typefile onChange{(e) handleUpload(e.target.files[0])} / {progress 0 progress value{progress} max100 /} /div ); }4. React 组件中的数据获取模式4.1 useEffect 模式function DataFetchingComponent() { const [data, setData] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { let isMounted true; const fetchData async () { try { const response await axios.get(/api/data); if (isMounted) { setData(response.data); setLoading(false); } } catch (err) { if (isMounted) { setError(err.message); setLoading(false); } } }; fetchData(); return () { isMounted false; }; }, []); if (loading) return Spinner /; if (error) return Error message{error} /; return DataDisplay data{data} /; }4.2 自定义 Hook 封装// useFetch Hook function useFetch(url, options {}) { const [data, setData] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { const abortController new AbortController(); const fetchData async () { try { setLoading(true); const response await fetch(url, { ...options, signal: abortController.signal, }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const result await response.json(); setData(result); setError(null); } catch (err) { if (err.name ! AbortError) { setError(err.message); } } finally { setLoading(false); } }; fetchData(); return () abortController.abort(); }, [url]); return { data, loading, error }; } // 使用自定义 Hook function UserProfile({ userId }) { const { data: user, loading, error } useFetch( https://api.example.com/users/${userId} ); if (loading) return Spinner /; if (error) return Error message{error} /; return div{user?.name}/div; }4.3 依赖参数的重新获取function UserPosts({ userId }) { const [posts, setPosts] useState([]); const [loading, setLoading] useState(false); useEffect(() { if (!userId) return; let isMounted true; setLoading(true); axios.get(/api/users/${userId}/posts) .then(response { if (isMounted) { setPosts(response.data); setLoading(false); } }) .catch(error { if (isMounted) { console.error(error); setLoading(false); } }); return () { isMounted false; }; }, [userId]); // userId 变化时重新获取 return ( div {loading ? Spinner / : PostList posts{posts} /} /div ); }4.4 条件请求function ConditionalFetch({ shouldFetch, userId }) { const [data, setData] useState(null); useEffect(() { if (!shouldFetch || !userId) return; axios.get(/api/users/${userId}) .then(response setData(response.data)); }, [shouldFetch, userId]); return div{data?.name}/div; }4.5 防抖搜索import { debounce } from lodash; function SearchComponent() { const [query, setQuery] useState(); const [results, setResults] useState([]); const [loading, setLoading] useState(false); const searchAPI useCallback( debounce(async (searchTerm) { if (!searchTerm) { setResults([]); setLoading(false); return; } setLoading(true); try { const response await axios.get(/api/search, { params: { q: searchTerm }, }); setResults(response.data); } catch (error) { console.error(error); } finally { setLoading(false); } }, 500), [] ); const handleChange (e) { const value e.target.value; setQuery(value); searchAPI(value); }; return ( div input typetext value{query} onChange{handleChange} placeholder搜索... / {loading div搜索中.../div} ul {results.map(item ( li key{item.id}{item.name}/li ))} /ul /div ); }5. 错误处理最佳实践5.1 全局错误处理// axios 全局错误处理 axios.interceptors.response.use( response response, error { // 网络错误 if (!error.response) { console.error(网络错误:, error.message); return Promise.reject(new Error(网络连接失败)); } // HTTP 状态码错误 const { status, data } error.response; switch (status) { case 400: console.error(请求参数错误:, data); break; case 401: console.error(未授权请重新登录); // 跳转到登录页 break; case 403: console.error(无权限访问); break; case 404: console.error(资源不存在); break; case 500: console.error(服务器错误); break; default: console.error(HTTP ${status}:, data); } return Promise.reject(error); } );5.2 重试机制async function fetchWithRetry(url, options {}, retries 3, delay 1000) { for (let i 0; i retries; i) { try { const response await fetch(url, options); if (!response.ok) { throw new Error(HTTP ${response.status}); } return response; } catch (error) { if (i retries - 1) throw error; await new Promise(resolve setTimeout(resolve, delay * Math.pow(2, i))); } } } // 使用 fetchWithRetry(https://api.example.com/unstable) .then(res res.json()) .then(data console.log(data)) .catch(err console.error(重试失败:, err));6. 性能优化6.1 请求缓存const cache new Map(); async function fetchWithCache(url) { if (cache.has(url)) { console.log(从缓存返回); return cache.get(url); } const response await fetch(url); const data await response.json(); cache.set(url, data); return data; }6.2 请求去重const pendingRequests new Map(); async function fetchWithDedupe(url) { if (pendingRequests.has(url)) { console.log(等待中的请求); return pendingRequests.get(url); } const promise fetch(url).then(res res.json()); pendingRequests.set(url, promise); try { const result await promise; return result; } finally { pendingRequests.delete(url); } }7. React 19 中的数据获取7.1 新的 use 钩子import { use } from react; // 创建 Promise const fetchUsers fetch(https://api.example.com/users) .then(res res.json()); function UserList() { // use 钩子会暂停渲染直到 Promise resolve const users use(fetchUsers); return ( ul {users.map(user ( li key{user.id}{user.name}/li ))} /ul ); } // 配合 Suspense 使用 function App() { return ( Suspense fallback{div加载中.../div} UserList / /Suspense ); }7.2 结合 Server Components// 在 Server Component 中直接使用 async/await async function ServerComponent() { const response await fetch(https://api.example.com/data); const data await response.json(); return ( div h1{data.title}/h1 p{data.description}/p /div ); }8. 完整示例用户管理面板import { useState, useEffect, useCallback } from react; import axios from axios; // API 客户端配置 const api axios.create({ baseURL: https://jsonplaceholder.typicode.com, timeout: 5000, }); // 请求拦截器 api.interceptors.request.use(config { console.log([${config.method?.toUpperCase()}] ${config.url}); return config; }); // 响应拦截器 api.interceptors.response.use( response response, error { console.error(API Error:, error.response?.status, error.message); return Promise.reject(error); } ); function UserManagement() { const [users, setUsers] useState([]); const [loading, setLoading] useState(true); const [error, setError] useState(null); const [formData, setFormData] useState({ name: , email: }); // 获取用户列表 const fetchUsers useCallback(async () { try { setLoading(true); const response await api.get(/users); setUsers(response.data); setError(null); } catch (err) { setError(err.message); } finally { setLoading(false); } }, []); // 创建用户 const createUser async (userData) { try { const response await api.post(/users, userData); setUsers(prev [...prev, response.data]); return response.data; } catch (err) { console.error(创建失败:, err); throw err; } }; // 更新用户 const updateUser async (id, userData) { try { const response await api.put(/users/${id}, userData); setUsers(prev prev.map(user user.id id ? { ...user, ...response.data } : user )); return response.data; } catch (err) { console.error(更新失败:, err); throw err; } }; // 删除用户 const deleteUser async (id) { try { await api.delete(/users/${id}); setUsers(prev prev.filter(user user.id ! id)); } catch (err) { console.error(删除失败:, err); throw err; } }; useEffect(() { fetchUsers(); }, [fetchUsers]); const handleSubmit async (e) { e.preventDefault(); await createUser(formData); setFormData({ name: , email: }); }; if (loading) return div加载用户列表.../div; if (error) return div错误: {error}/div; return ( div h1用户管理/h1 {/* 创建表单 */} form onSubmit{handleSubmit} input typetext placeholder姓名 value{formData.name} onChange{(e) setFormData({ ...formData, name: e.target.value })} / input typeemail placeholder邮箱 value{formData.email} onChange{(e) setFormData({ ...formData, email: e.target.value })} / button typesubmit创建用户/button /form {/* 用户列表 */} ul {users.map(user ( li key{user.id} {user.name} - {user.email} button onClick{() deleteUser(user.id)}删除/button /li ))} /ul /div ); } export default UserManagement;9. 总结选择建议场景推荐理由简单项目、原型fetch原生支持无需额外依赖大型项目、复杂需求axios功能丰富拦截器、取消请求等需要上传进度axios原生支持 onUploadProgress需要超时控制axios原生支持 timeout 配置需要兼容老浏览器axios自动 polyfillReact 19 Suspensefetch use原生支持配合新特性决策树需要数据获取 │ ▼ 项目规模 │ ┌───┴───┐ 小 大 │ │ ▼ ▼ fetch axios │ │ └───┬───┘ ▼ 是否需要 高级特性 │ ┌───┴───┐ 是 否 │ │ ▼ ▼ axios fetch记忆口诀数据获取两把刀fetch 原生 axios 强简单用 fetch 轻巧复杂用 axios 功能全拦截取消都支持上传进度更流畅10. 相关资源MDN fetch 文档axios 官方文档React 数据获取最佳实践React 19 use 钩子11. 练习基础练习使用 fetch 创建一个天气查询组件使用 axios 实现一个 GitHub 用户搜索功能进阶练习实现一个带有请求缓存和去重的数据获取 Hook创建一个支持自动重试和错误边界的数据获取组件
04-性能优化与最佳实践——11. 数据获取 - fetch 与 axios
发布时间:2026/6/25 12:26:56
11. 数据获取 - fetch 与 axios概述数据获取是现代 React 应用的核心需求。fetch是浏览器原生 APIaxios是第三方 HTTP 客户端库。两者都能发送 HTTP 请求但在功能、语法和易用性上有所不同。维度内容What使用 fetch API 或 axios 库从服务器获取数据Why从后端 API 获取数据动态更新 UIWhen组件需要从服务器加载数据时WhereuseEffect 钩子中、事件处理函数中Who需要与服务端交互的开发者Howfetch(url).then(res res.json())或axios.get(url)1. fetch vs axios 对比1.1 快速对比表特性fetchaxios浏览器支持原生需要 polyfill 支持 IE所有浏览器包括 IE语法基于 Promise基于 PromiseJSON 处理需要手动.json()自动转换请求拦截不支持原生支持响应拦截不支持原生支持取消请求使用 AbortController支持 CancelToken超时设置不支持原生支持上传进度需要手动实现支持体积0KB原生~13KB压缩后错误处理只捕获网络错误捕获 HTTP 错误状态码1.2 基础语法对比// fetch 版本 fetch(https://api.example.com/users) .then(response { if (!response.ok) { throw new Error(HTTP error response.status); } return response.json(); }) .then(data console.log(data)) .catch(error console.error(error)); // axios 版本 axios.get(https://api.example.com/users) .then(response console.log(response.data)) .catch(error console.error(error));2. fetch API 详解2.1 基础 GET 请求import { useState, useEffect } from react; function FetchUsers() { const [users, setUsers] useState([]); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { fetch(https://jsonplaceholder.typicode.com/users) .then(response { if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } return response.json(); }) .then(data { setUsers(data); setLoading(false); }) .catch(err { setError(err.message); setLoading(false); }); }, []); if (loading) return div加载中.../div; if (error) return div错误: {error}/div; return ( ul {users.map(user ( li key{user.id}{user.name}/li ))} /ul ); }2.2 POST 请求function CreatePost() { const [title, setTitle] useState(); const [body, setBody] useState(); const [status, setStatus] useState(idle); const handleSubmit async (e) { e.preventDefault(); setStatus(loading); try { const response await fetch(https://jsonplaceholder.typicode.com/posts, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ title, body, userId: 1, }), }); if (!response.ok) { throw new Error(创建失败); } const data await response.json(); console.log(创建成功:, data); setStatus(success); setTitle(); setBody(); } catch (error) { console.error(错误:, error); setStatus(error); } }; return ( form onSubmit{handleSubmit} input typetext value{title} onChange{(e) setTitle(e.target.value)} placeholder标题 / textarea value{body} onChange{(e) setBody(e.target.value)} placeholder内容 / button typesubmit disabled{status loading} {status loading ? 提交中... : 提交} /button /form ); }2.3 PUT 和 DELETE 请求// PUT 更新 async function updateUser(id, userData) { const response await fetch(https://api.example.com/users/${id}, { method: PUT, headers: { Content-Type: application/json, }, body: JSON.stringify(userData), }); if (!response.ok) { throw new Error(更新失败); } return response.json(); } // DELETE 删除 async function deleteUser(id) { const response await fetch(https://api.example.com/users/${id}, { method: DELETE, }); if (!response.ok) { throw new Error(删除失败); } return true; }2.4 带参数的 GET 请求// 查询参数 function SearchUsers({ query }) { const [results, setResults] useState([]); useEffect(() { const url new URL(https://api.example.com/users); url.searchParams.append(q, query); url.searchParams.append(limit, 10); fetch(url) .then(res res.json()) .then(data setResults(data)); }, [query]); return UserList users{results} /; }2.5 请求取消AbortControllerfunction CancelableFetch() { const [data, setData] useState(null); useEffect(() { const abortController new AbortController(); fetch(https://api.example.com/slow-endpoint, { signal: abortController.signal, }) .then(res res.json()) .then(data setData(data)) .catch(err { if (err.name AbortError) { console.log(请求已取消); } else { console.error(错误:, err); } }); // 组件卸载时取消请求 return () abortController.abort(); }, []); return div{JSON.stringify(data)}/div; }2.6 超时处理手动实现async function fetchWithTimeout(url, options {}, timeout 5000) { const controller new AbortController(); const { signal } controller; const timeoutId setTimeout(() controller.abort(), timeout); try { const response await fetch(url, { ...options, signal }); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); if (error.name AbortError) { throw new Error(请求超时); } throw error; } } // 使用 fetchWithTimeout(https://api.example.com/data, {}, 3000) .then(res res.json()) .then(data console.log(data)) .catch(err console.error(err.message));3. axios 详解3.1 安装与配置npminstallaxiosimport axios from axios; // 全局配置 axios.defaults.baseURL https://api.example.com; axios.defaults.timeout 5000; axios.defaults.headers.common[Authorization] Bearer token; axios.defaults.headers.post[Content-Type] application/json;3.2 基础 GET 请求import { useState, useEffect } from react; import axios from axios; function AxiosUsers() { const [users, setUsers] useState([]); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { axios.get(https://jsonplaceholder.typicode.com/users) .then(response { setUsers(response.data); setLoading(false); }) .catch(err { setError(err.message); setLoading(false); }); }, []); if (loading) return div加载中.../div; if (error) return div错误: {error}/div; return ( ul {users.map(user ( li key{user.id}{user.name}/li ))} /ul ); }3.3 POST 请求async function createPost(postData) { try { const response await axios.post( https://jsonplaceholder.typicode.com/posts, postData, { headers: { Content-Type: application/json, }, } ); console.log(创建成功:, response.data); return response.data; } catch (error) { console.error(创建失败:, error.response?.data || error.message); throw error; } } // 使用 createPost({ title: My Post, body: This is the content, userId: 1 });3.4 并发请求async function fetchMultipleData() { try { const [usersResponse, postsResponse, commentsResponse] await Promise.all([ axios.get(https://api.example.com/users), axios.get(https://api.example.com/posts), axios.get(https://api.example.com/comments), ]); return { users: usersResponse.data, posts: postsResponse.data, comments: commentsResponse.data, }; } catch (error) { console.error(并发请求失败:, error); throw error; } }3.5 请求拦截器// 请求拦截器 axios.interceptors.request.use( config { // 在请求发送前做些什么 console.log(请求发送:, config.url); // 添加 token const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }, error { return Promise.reject(error); } ); // 响应拦截器 axios.interceptors.response.use( response { // 对响应数据做点什么 console.log(响应接收:, response.config.url); return response; }, error { // 对响应错误做点什么 if (error.response?.status 401) { // 未授权跳转到登录页 window.location.href /login; } return Promise.reject(error); } );3.6 创建实例// 创建多个实例不同配置 const apiClient axios.create({ baseURL: https://api.example.com, timeout: 5000, headers: { Content-Type: application/json, }, }); const uploadClient axios.create({ baseURL: https://upload.example.com, timeout: 30000, headers: { Content-Type: multipart/form-data, }, }); // 使用实例 apiClient.get(/users).then(res console.log(res.data)); uploadClient.post(/image, formData).then(res console.log(res.data));3.7 取消请求import { CancelToken } from axios; function CancelableAxios() { useEffect(() { const source CancelToken.source(); axios.get(https://api.example.com/slow-endpoint, { cancelToken: source.token, }) .then(response console.log(response.data)) .catch(err { if (axios.isCancel(err)) { console.log(请求已取消:, err.message); } else { console.error(错误:, err); } }); return () source.cancel(组件卸载取消请求); }, []); }3.8 上传进度function UploadFile() { const [progress, setProgress] useState(0); const handleUpload async (file) { const formData new FormData(); formData.append(file, file); try { const response await axios.post(/upload, formData, { headers: { Content-Type: multipart/form-data, }, onUploadProgress: (progressEvent) { const percentCompleted Math.round( (progressEvent.loaded * 100) / progressEvent.total ); setProgress(percentCompleted); }, }); console.log(上传成功:, response.data); } catch (error) { console.error(上传失败:, error); } }; return ( div input typefile onChange{(e) handleUpload(e.target.files[0])} / {progress 0 progress value{progress} max100 /} /div ); }4. React 组件中的数据获取模式4.1 useEffect 模式function DataFetchingComponent() { const [data, setData] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { let isMounted true; const fetchData async () { try { const response await axios.get(/api/data); if (isMounted) { setData(response.data); setLoading(false); } } catch (err) { if (isMounted) { setError(err.message); setLoading(false); } } }; fetchData(); return () { isMounted false; }; }, []); if (loading) return Spinner /; if (error) return Error message{error} /; return DataDisplay data{data} /; }4.2 自定义 Hook 封装// useFetch Hook function useFetch(url, options {}) { const [data, setData] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { const abortController new AbortController(); const fetchData async () { try { setLoading(true); const response await fetch(url, { ...options, signal: abortController.signal, }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const result await response.json(); setData(result); setError(null); } catch (err) { if (err.name ! AbortError) { setError(err.message); } } finally { setLoading(false); } }; fetchData(); return () abortController.abort(); }, [url]); return { data, loading, error }; } // 使用自定义 Hook function UserProfile({ userId }) { const { data: user, loading, error } useFetch( https://api.example.com/users/${userId} ); if (loading) return Spinner /; if (error) return Error message{error} /; return div{user?.name}/div; }4.3 依赖参数的重新获取function UserPosts({ userId }) { const [posts, setPosts] useState([]); const [loading, setLoading] useState(false); useEffect(() { if (!userId) return; let isMounted true; setLoading(true); axios.get(/api/users/${userId}/posts) .then(response { if (isMounted) { setPosts(response.data); setLoading(false); } }) .catch(error { if (isMounted) { console.error(error); setLoading(false); } }); return () { isMounted false; }; }, [userId]); // userId 变化时重新获取 return ( div {loading ? Spinner / : PostList posts{posts} /} /div ); }4.4 条件请求function ConditionalFetch({ shouldFetch, userId }) { const [data, setData] useState(null); useEffect(() { if (!shouldFetch || !userId) return; axios.get(/api/users/${userId}) .then(response setData(response.data)); }, [shouldFetch, userId]); return div{data?.name}/div; }4.5 防抖搜索import { debounce } from lodash; function SearchComponent() { const [query, setQuery] useState(); const [results, setResults] useState([]); const [loading, setLoading] useState(false); const searchAPI useCallback( debounce(async (searchTerm) { if (!searchTerm) { setResults([]); setLoading(false); return; } setLoading(true); try { const response await axios.get(/api/search, { params: { q: searchTerm }, }); setResults(response.data); } catch (error) { console.error(error); } finally { setLoading(false); } }, 500), [] ); const handleChange (e) { const value e.target.value; setQuery(value); searchAPI(value); }; return ( div input typetext value{query} onChange{handleChange} placeholder搜索... / {loading div搜索中.../div} ul {results.map(item ( li key{item.id}{item.name}/li ))} /ul /div ); }5. 错误处理最佳实践5.1 全局错误处理// axios 全局错误处理 axios.interceptors.response.use( response response, error { // 网络错误 if (!error.response) { console.error(网络错误:, error.message); return Promise.reject(new Error(网络连接失败)); } // HTTP 状态码错误 const { status, data } error.response; switch (status) { case 400: console.error(请求参数错误:, data); break; case 401: console.error(未授权请重新登录); // 跳转到登录页 break; case 403: console.error(无权限访问); break; case 404: console.error(资源不存在); break; case 500: console.error(服务器错误); break; default: console.error(HTTP ${status}:, data); } return Promise.reject(error); } );5.2 重试机制async function fetchWithRetry(url, options {}, retries 3, delay 1000) { for (let i 0; i retries; i) { try { const response await fetch(url, options); if (!response.ok) { throw new Error(HTTP ${response.status}); } return response; } catch (error) { if (i retries - 1) throw error; await new Promise(resolve setTimeout(resolve, delay * Math.pow(2, i))); } } } // 使用 fetchWithRetry(https://api.example.com/unstable) .then(res res.json()) .then(data console.log(data)) .catch(err console.error(重试失败:, err));6. 性能优化6.1 请求缓存const cache new Map(); async function fetchWithCache(url) { if (cache.has(url)) { console.log(从缓存返回); return cache.get(url); } const response await fetch(url); const data await response.json(); cache.set(url, data); return data; }6.2 请求去重const pendingRequests new Map(); async function fetchWithDedupe(url) { if (pendingRequests.has(url)) { console.log(等待中的请求); return pendingRequests.get(url); } const promise fetch(url).then(res res.json()); pendingRequests.set(url, promise); try { const result await promise; return result; } finally { pendingRequests.delete(url); } }7. React 19 中的数据获取7.1 新的 use 钩子import { use } from react; // 创建 Promise const fetchUsers fetch(https://api.example.com/users) .then(res res.json()); function UserList() { // use 钩子会暂停渲染直到 Promise resolve const users use(fetchUsers); return ( ul {users.map(user ( li key{user.id}{user.name}/li ))} /ul ); } // 配合 Suspense 使用 function App() { return ( Suspense fallback{div加载中.../div} UserList / /Suspense ); }7.2 结合 Server Components// 在 Server Component 中直接使用 async/await async function ServerComponent() { const response await fetch(https://api.example.com/data); const data await response.json(); return ( div h1{data.title}/h1 p{data.description}/p /div ); }8. 完整示例用户管理面板import { useState, useEffect, useCallback } from react; import axios from axios; // API 客户端配置 const api axios.create({ baseURL: https://jsonplaceholder.typicode.com, timeout: 5000, }); // 请求拦截器 api.interceptors.request.use(config { console.log([${config.method?.toUpperCase()}] ${config.url}); return config; }); // 响应拦截器 api.interceptors.response.use( response response, error { console.error(API Error:, error.response?.status, error.message); return Promise.reject(error); } ); function UserManagement() { const [users, setUsers] useState([]); const [loading, setLoading] useState(true); const [error, setError] useState(null); const [formData, setFormData] useState({ name: , email: }); // 获取用户列表 const fetchUsers useCallback(async () { try { setLoading(true); const response await api.get(/users); setUsers(response.data); setError(null); } catch (err) { setError(err.message); } finally { setLoading(false); } }, []); // 创建用户 const createUser async (userData) { try { const response await api.post(/users, userData); setUsers(prev [...prev, response.data]); return response.data; } catch (err) { console.error(创建失败:, err); throw err; } }; // 更新用户 const updateUser async (id, userData) { try { const response await api.put(/users/${id}, userData); setUsers(prev prev.map(user user.id id ? { ...user, ...response.data } : user )); return response.data; } catch (err) { console.error(更新失败:, err); throw err; } }; // 删除用户 const deleteUser async (id) { try { await api.delete(/users/${id}); setUsers(prev prev.filter(user user.id ! id)); } catch (err) { console.error(删除失败:, err); throw err; } }; useEffect(() { fetchUsers(); }, [fetchUsers]); const handleSubmit async (e) { e.preventDefault(); await createUser(formData); setFormData({ name: , email: }); }; if (loading) return div加载用户列表.../div; if (error) return div错误: {error}/div; return ( div h1用户管理/h1 {/* 创建表单 */} form onSubmit{handleSubmit} input typetext placeholder姓名 value{formData.name} onChange{(e) setFormData({ ...formData, name: e.target.value })} / input typeemail placeholder邮箱 value{formData.email} onChange{(e) setFormData({ ...formData, email: e.target.value })} / button typesubmit创建用户/button /form {/* 用户列表 */} ul {users.map(user ( li key{user.id} {user.name} - {user.email} button onClick{() deleteUser(user.id)}删除/button /li ))} /ul /div ); } export default UserManagement;9. 总结选择建议场景推荐理由简单项目、原型fetch原生支持无需额外依赖大型项目、复杂需求axios功能丰富拦截器、取消请求等需要上传进度axios原生支持 onUploadProgress需要超时控制axios原生支持 timeout 配置需要兼容老浏览器axios自动 polyfillReact 19 Suspensefetch use原生支持配合新特性决策树需要数据获取 │ ▼ 项目规模 │ ┌───┴───┐ 小 大 │ │ ▼ ▼ fetch axios │ │ └───┬───┘ ▼ 是否需要 高级特性 │ ┌───┴───┐ 是 否 │ │ ▼ ▼ axios fetch记忆口诀数据获取两把刀fetch 原生 axios 强简单用 fetch 轻巧复杂用 axios 功能全拦截取消都支持上传进度更流畅10. 相关资源MDN fetch 文档axios 官方文档React 数据获取最佳实践React 19 use 钩子11. 练习基础练习使用 fetch 创建一个天气查询组件使用 axios 实现一个 GitHub 用户搜索功能进阶练习实现一个带有请求缓存和去重的数据获取 Hook创建一个支持自动重试和错误边界的数据获取组件