cover

從 XMLHttpRequest 到 React Query,前端跟 API 打交道的方式進化了好幾代。

先講結論

如果你只想記一個結論:簡單請求用原生 fetch,需要攔截器或統一錯誤處理用 axios,React/Vue 專案有超過三個 API 呼叫就用 React Query 或 SWR。剩下的看完這篇你就知道為什麼了。

XHR:遠古時代的武器

XMLHttpRequest(XHR)是最早的非同步請求方式,2005 年 Gmail 用它實現了不用整頁刷新就能收信的功能,當時驚為天人。但語法真的很繁瑣:

const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users');
xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log(JSON.parse(xhr.responseText));
  }
};
xhr.onerror = function() { console.error('炸了'); };
xhr.send();

現在幾乎不會直接寫 XHR 了,但它有一個 fetch 做不到的事:追蹤上傳進度。所以如果你需要做上傳進度條,XHR 還是有存在的價值。

Fetch API:現代標準

2015 年出來的原生 API,基於 Promise,語法簡潔很多:

// GET
const response = await fetch('/api/users');
const users = await response.json();
 
// POST
await fetch('/api/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'John' })
});

但 fetch 有兩個新手必踩的坑:

坑一:HTTP 4xx/5xx 不會 reject。fetch('/api/not-found') 回傳 404,它不會進 catch——你得自己檢查 response.ok。這個設計哲學是「只有網路錯誤才是真的錯誤」,但實務上真的很容易忘記檢查。

坑二:預設不帶 Cookie。跨域請求要加 credentials: 'include',同源也要加 credentials: 'same-origin'

async function fetchUsers() {
  try {
    const response = await fetch('/api/users');
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

取消請求用 AbortController——在 React 的 useEffect cleanup 裡特別重要,不然使用者快速切換頁面時,可能會更新到已卸載元件的 state。

Axios:fetch 的豪華版

需要裝套件,但功能完整很多:

const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
});
 
// 請求攔截器:自動帶 Token
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});
 
// 回應攔截器:統一處理 401
api.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

Axios 最大的優勢是攔截器——自動帶 Token、統一錯誤處理、自動 JSON 轉換,這些 fetch 都要自己手工做。

Promise 跟 async/await

這兩個不是「發請求的工具」,而是「處理非同步操作的語法」。不管你用 fetch 還是 axios,底層都靠 Promise。

// Promise 並行(同時發多個請求)
const [users, posts] = await Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json())
]);

async/await 就是 Promise 的語法糖,讓非同步程式碼看起來像同步。但記住一個常見的效能問題:如果兩個請求沒有依賴關係,不要用 await 串行執行,用 Promise.all 並行才是對的。

React Query / SWR:前端資料請求的終極形態

上面的工具都只解決了「怎麼發請求」。但實際的 React/Vue 專案裡,你還得管 loading 狀態、error 狀態、快取、重新請求、分頁…全部手動管理會讓你崩潰。

// React Query — 一個 hook 搞定所有狀態
const { data, isLoading, error } = useQuery({
  queryKey: ['users'],
  queryFn: () => fetch('/api/users').then(r => r.json()),
  staleTime: 5 * 60 * 1000, // 5 分鐘內不重新請求
});

SWR 是 Vercel 出的替代方案,API 更簡單(useSWR(url, fetcher) 就搞定),適合不需要太多進階功能的專案。React Query 功能更完整,有 Devtools、離線支援、完整的 Mutation 管理。

我的建議:app 有超過三個 API 呼叫,就不要自己管 loading state 了。React Query 或 SWR 省下來的時間和少寫的 bug,絕對值得多裝一個套件。

怎麼選?

場景推薦
簡單一次性請求fetch
需要攔截器/統一錯誤處理axios
React/Vue 專案的資料請求React Query / SWR
檔案上傳需要進度條axios 或 XHR
SSR / Server Componentfetch(Next.js 原生支援)

工具的選擇不難,難的是知道什麼時候該用什麼。好消息是這篇看完你就知道了。

延伸閱讀

MDN - Fetch API Axios 官方文件 TanStack Query 官方文件 SWR 官方文件 RESTFul API API CRUD