在前面Vuex資料流中呼叫actions時,其中一個步驟,即EmployeeService.query(params)
,乃是向API請求資料,相關步驟程式碼位置如下:
// src/store/employee.module.js
import { EmployeeService } from "@/common/api.service";
async [FETCH_EMPLOYEES](context, params) {
context.commit(SET_LOADING, true);
try {
const { data } = await EmployeeService.query(params); // 呼叫API拿資料
...
} catch (err) {
...
} finally {
...
}
}
不同於Vuex狀態管理檔案,主要放置於src/store
,該API請求檔案,乃放置於src/common/api.service.js
,以下提供簡易Vue專案結構供區分參考。
vue-app-project/
├─ public/
├─ src/
│ ├─ common/ # 放置共享資源,如共用元件、工具類程式、服務類程式
│ │ ├─ api.service.js # API請求相關程式碼
│ ├─ store/ # 放置狀態管理,如Vuex相關檔案
│ │ ├─ employee.module.js # employee模組物件
打開api.service.js
檔案,相關程式碼參考如下:
// src/common/api.service.js
import Vue from "vue";
import axios from "axios";
import VueAxios from "vue-axios";
import { API_URL } from "@/common/config";
// 封裝axios相關設定與基礎操作(如query)
const ApiService = {
// 初始化,註冊axios作為Vue插件,並設定預設baseURL
init() {
Vue.use(VueAxios, axios);
Vue.axios.defaults.baseURL = API_URL;
},
// 組成GET請求的完整網址,並用axios.get()發送請求
// 這是EmployeeService.query()背後真正執行的方法
query(resource, params) {
// 驗證參數是否為物件,確保URL查詢字串能正確組成
if (typeof params !== "object") {
throw new Error(`[ApiService] params 必須是物件,你傳的是:${typeof params}`);
}
// 將參數物件(params)轉為查詢字串,不直接使用,可用於除錯
// 查詢字串,舉例如:page=1&page_size=10
const queryString = new URLSearchParams(params).toString();
// 組出完整請求網址,不直接使用,可用於除錯
// 完整請求網址,舉例如:
// https://api.example.com/employee/?page=1&page_size=10
const fullUrl = `${Vue.axios.defaults.baseURL}${resource}?${queryString}`;
console.log(`QUERY 請求網址:`, fullUrl);
// 真正送出並執行GET請求,params會自動被axios加入為「?key=value」格式之查詢參數
return Vue.axios.get(resource, { params }).catch((error) => {
throw new Error(`[RWV] ApiService ${error}`);
});
},
};
export default ApiService;
// 封裝「與員工相關」的API呼叫
export const EmployeeService = {
// 取得員工列表,可帶分頁或篩選參數(params)
// 實際上就是呼叫ApiService.query(),但固定resource為"employee/"
query(params) {
return ApiService.query("employee/", params);
},
};
其中可以發現EmployeeService.query(params)
,其實還另外呼叫ApiService.query(resource, params)
,進行API服務,此乃封裝axios的共同邏輯,並讓各領域服務,如Employee,統一透過它發送API請求。
進一步介紹,axios是一個用於發送HTTP請求的JavaScript庫,可完成跟後端API請求及發送資料的功能,包括GET、POST、PUT、DELETE基本功能,並可接收回傳資料。
而EmployeeService
藉由固定resource為”employee/”
,僅針對員工相關API,如query(params)
,進行呼叫,以集中區分管理,因此Vuex模組物件只要呼叫EmployeeService
,而不用理會其中的底層邏輯。
在設定ApiService之前,需要進行初始化,將axios註冊作為Vue插件,並設定預設baseURL,提供API請求位址。需要注意的是,於專案啟動時,在src/main.js
中需要呼叫ApiService.init()
,以進行ApiService的設定。
// src/main.js
import ApiService from "./common/api.service";
ApiService.init();
ApiService .query(resource, params)
,乃使用典型的axios.get()
+查詢參數的API請求。
queryString
組合出錯。URLSearchParams()
及queryString
所組合出的查詢字串fullUrl
,只用於除錯,可供給console.log()
輸出,以達到可解釋性除錯。axios.get()
進行,axios會自動把{ params }
物件轉成URL查詢參數,並處理URL編碼,如空格、特殊符號等,如此可確保正確編碼,並順利取得所回傳的資料。const ApiService = {
// 組成GET請求的完整網址,並用axios.get()發送請求
// 這是EmployeeService.query()背後真正執行的方法
query(resource, params) {
// 驗證參數是否為物件,確保URL查詢字串能正確組成
if (typeof params !== "object") {
throw new Error(`[ApiService] params 必須是物件,你傳的是:${typeof params}`);
}
// 將參數物件(params)轉為查詢字串,不直接使用,可用於除錯
// 查詢字串,舉例如:page=1&page_size=10
const queryString = new URLSearchParams(params).toString();
// 組出完整請求網址,不直接使用,可用於除錯
// 完整請求網址,舉例如:
// https://api.example.com/employee/?page=1&page_size=10
const fullUrl = `${Vue.axios.defaults.baseURL}${resource}?${queryString}`;
console.log(`QUERY 請求網址:`, fullUrl);
// 真正送出並執行GET請求,params會自動被axios加入為「?key=value」格式之查詢參數
return Vue.axios.get(resource, { params }).catch((error) => {
throw new Error(`[RWV] ApiService ${error}`);
});
},
};
閱讀到此,其實先前在組件中以dispatch()
所呼叫的action,本質上仍然是透過axios向後端API發送請求,只是先將axios行為統一封裝成ApiService,讓各不同領域組件可共同使用,並以固定resource的方式進行細分服務,達到區分管理的作用,並可使Vuex模組物件能方便、簡潔地進行後端API請求。
最後,提供一個小技巧,在src/common/api.service.js
檔案中,所使用到的API請求網址,因為涉及到各資訊安全防護等敏感議題,因此我們通常都是從外部檔案,如src/common/config.js
,引入API_URL
常數,這樣的好處除了可以集中管理環境設定,並在不同環境(開發、測試、正式)切換時更方便,並且因該外部檔案通常會設定不公開,如此在公開的api.service.js
檔案中,就可以避免伺服器位址暴露的風險。不過需注意,前端打包後的API_URL
還是能被看到,若有真正高敏感資料,仍應該放在後端環境變數中處理。