在開發 Web 應用程式時,axios.create() 是一個非常強大且常用的功能。簡單來說,它允許你建立一個 Axios 的自定義實體 (Instance)。
為什麼要這樣做呢?因為在大型專案中,你可能需要連接不同的 API 伺服器,或者希望為所有的請求設定統一的標準(例如:超時時間、特定的 Header 或基礎路徑)。
1. 基本語法
你可以透過傳遞一個配置物件(config)來建立實體:
const apiClient = axios.create({
baseURL: '<https://api.example.com>',
timeout: 5000, // 超過 5 秒則判定為失敗
headers: {'X-Custom-Header': 'foobar'}
});建立之後,你就可以像使用原本的 axios 一樣使用 apiClient:
apiClient.get('/users')
.then(response => console.log(response.data));
// 實際請求路徑為:<https://api.example.com/users>2. 為什麼要使用axios.create()?
使用實體化主要有以下三個好處:
A. 統一管理配置 (Configuration)
如果你的 API 都在同一個域名下,你不需要在每個請求都寫完整的 URL。只需設定一次 baseURL。
B. 區分不同的 API 來源
如果你的專案同時需要呼叫 Google API 和 自家的後端 API,你可以建立兩個不同的實體:
googleDriveClient: 設定 Google 的驗證與路徑。internalServerClient: 設定自家伺服器的路徑與超時規則。
C. 獨立的攔截器 (Interceptors)
這是最重要的功能。你可以為不同的實體設定不同的請求攔截器或回應攔截器。例如:在「後端實體」中統一加入 JWT Token,而在「外部 API 實體」中則不加入。
3. 實戰範例:封裝 API 模組
在開發中,我們通常會將 API 實體獨立成一個檔案(如 request.js):
// utils/request.js
import axios from 'axios';
const service = axios.create({
baseURL: process.env.VITE_API_URL, // 從環境變數讀取路徑
timeout: 10000
});
// 請求攔截器:在發送請求前做些什麼(例如加上 Token)
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
// 回應攔截器:統一處理錯誤訊息
service.interceptors.response.use(
response => response.data, // 直接回傳 data 內容,簡化後續呼叫
error => {
alert('發生錯誤:' + error.response.status);
return Promise.reject(error);
}
);
export default service;
4. 常用配置項一覽
| 配置項 | 說明 | 範例 |
|---|---|---|
baseURL | 請求的基礎路徑,會自動拼接到 URL 前面 | '<https://api.site.com>' |
timeout | 設定請求超時(毫秒) | 5000 |
headers | 自定義的請求頭 | {'Content-Type': 'application/json'} |
withCredentials | 跨域請求時是否攜帶 Cookie | true / false |
5. 實戰應用
一個專案裡面不會只使用 1、2 支API,如果我們不希望這些 API 的路徑或是方法散落在專案各處,我們可以使用以下方法統一管理
// api/http.js
import axios from "axios";
// 引入 Authorization
import requestHelper from "@/utils/requestHelper";
const http = axios.create({
baseURL: privateDomain, // 共用的統一路徑
timeout: 15000,
});
// request 攔截
http.interceptors.request.use(
(config) => {
config.headers = {
...config.headers,
...requestHelper.setAuthorizationHeader(),
};
return config;
},
(error) => Promise.reject(error)
);
// response 攔截
http.interceptors.response.use(
(response) => response.data, // 統一只回 data
(error) => requestHelper.handleError(error)
);
export default http;
打 API 的方式改為函式統一管理:
// api/request.js
import http from "./http";
// 第一支 API
export const getFirstAPI = () =>
http.get("Type/firstAPI?$format=JSON");
// 第二支 API
export const getSecondAPI = () =>
http.get("Common/secondAPI?$format=JSON");
// 第三支 API
export const getThirdAPI = () =>
http.get("IndustrialPark/thirdAPI?$format=JSON");
// 第四支 API
export const getFourthAPI = () =>
http.get("Common/fourthAPI?$format=JSON");
實際在使用時,只要引入我們定義好的 API 函式即可。
import { getFirstAPI } from "@/api/request";
const fetchData = async () => {
const data = await getFirstAPI();
console.log(data);
};
之前踩過的坑
以前我會這樣寫,但是發現在一個檔案中,API 會觸發 2 次。
// request.js
export const firstApi = axios
.get(url, config)
.catch(handleError);
這樣的寫法等於:
// request.js
const firstApi = axios.get(...);// 立刻執行
export {firstApi };
所以在 import 之前就會打 API,而在程式中實際打 API 取資料時又會打一次 API,反而增加了無謂的請求次數:
// request.js
axios.get(...) ← HTTP request 已經送出
↓
回傳 Promise
↓
Promise 被 export
總結
axios.create() 就像是幫你打造一台專屬的請求機器。你設定好它的出發點(baseURL)、運作規則(timeout)以及檢查標準(interceptors),之後在程式碼各處呼叫它時,就不必再重複撰寫這些瑣碎的設定。
您是正在為專案進行 API 封裝嗎?如果是的話,我可以幫您示範如何根據特定的 API 文件撰寫對應的請求函式。
