[JS] 利用 ES6 的 Classes 切割 API Data 建立乾淨的架構

by Mesak

前端開發中的時候,很容易把 API 事件寫在同一個 component 裡面,大部分人會進行封裝,讓傳送 API 的方法變得更簡單

先前碰到同事裡用前端框架系統 Vue 的時候,則是把 API 放到 Vuex 裡面,之前看過 React 裡面,如果上下層傳遞有點過於遙遠或是複雜,通常會用 State 來管控, Vue 使用 Vuex ,React 則是用 redux 來表達貫穿這個元件的參數。

不過正如上面說的 Vuex 跟 redux 都是用來放狀態的,如果拿來放其他東西,並不是說不可以,只是如果在物件導向的裡面中,這樣就違反了 單一功能原則的項目,狀態管理就不再是狀態管理。

自從看了一點 Clean Code的書之後,開始針對 Clean Architecture 很感興趣,原本我主要寫後端跟一點點的前端,開始把後端架構邏輯設計延伸到了前端,開始修正前端同事的程式碼。

Clean Architecture:

Clean Architectur
From Layers and Inversion of Control

回到正題 要實現乾淨的架設設計,首先先把專案中的 Vuex 裡面,包了一堆 API 的模式給抽離開來,去年我重新理解一次 新的 JavaScript 的時候,發現 ES6 有個相當好用的東西叫做 Classes 如果你懂一點物件導向就可以知道這個東西是用來做什麼的。

實際操作了一下真的讓我感動到哭,很久很久以前操作 JavaScript 的時候要實現到類似的事情必須要 function 一層包了一層才有可能實現這件事情,現在原生 JS 支援把這個東西當成特別的 function 讓你使用更簡潔的方式建立物件,以及處理繼承,真的相當方便

這邊先設計一個範例,採用 https://reqres.in 來顯示 REST API 的方式,這個資源是隨意 Google到的,要套用現有的 資源應該也不是問題。

範例

JavaScript 的 REST API 範例 https://codesandbox.io/s/simple-restapi-mc4epc

Vue 3 的 REST API 範例 https://codesandbox.io/s/simple-restapi-vue-gcilc6

說明

首先先把 API 封裝,網路受有不少封裝教學,根據你專案所需要的封裝的內容,各有差異,這邊就不多做解釋,最多封裝的套件應該就是 axios 了 ,可以參考範例的 codesandbox,/src/utils/axios.js

程式中最終輸出了 export { axios, api }; 這一點主要是,你永遠不知道什麼時候會換一組 api 來打,所以把 axios 輸出,可以快速的重複取用,正常主網站會走 api 這個變數,如果你想叫 http 、 request 變數都可以,只要可以接上就行。

接著建立一組 Base Class , src/api/index.js,這檔案是所有 API 的底層,所有 API 都引入他,如果這支 Class 他擴充了新的 function ,其他繼承的子類別都會擁有一樣的 function,所以要處理往後端打的 參數,網址等事項處理都相當方便,另外他在啟動的時候會賦予一些基本的參數,定義在 constructor 這個 function,所以基本我給他定一個 method = ‘get’ 、接著是路徑,其他可根據需求調整。

這邊定義了一隻 action functon,主要是呼叫 axios 封裝後的 api,基本上這 function 會組成你需要的參數,接著回傳 axios。

呼叫範例都採用 /src/api/User.js 這隻來參考,User 這隻 API 可以看到一些固定的 function 跟 傳送的路徑名稱、method

因為我的後端採用 Laravel 的設計,基本預設的 REST API resource 的幾隻 method 就是 index、store、update、delete ,所以我照這幾個名稱加上參考 Google 的 API design guide ,裡面有清楚建議一些 API 的統一規範,如果你的專案有辦法所有團隊統一,那麼這些 API Function 就可以很固定。

User.js 的末段是 export default new User(); 你可能會問怎麼跟 Base 的回傳 Class Name 不同,這是因為 Base 設計用來繼承, User.js 呼叫的時候是用來實作的,所以後面呼叫的時候你可以任意命名給 User.js ,這樣你就不會針對同名不同資料夾的 User.js 給搞混,只需要給他一個新的名稱就可以呼叫。

另外,既然是繼承,你也可以在 Base 與 User 中間再加上一層 Class,例如叫做 System OR Backend Class,這樣 繼承這個名稱的類別又可以有另一層的操作,像是後台操作很多都是固定的 CRUD,以及列 LIST,你就可以把上述 index、store、update、delete 放在 Baskend 裡面的 Class,假如你有 10 個後臺頁面都是一樣操作,這樣就省了 4 個 method * 10 次的撰寫,每一隻頁面 API 都只需要命名以及定義好 path 就可以了,當然只是一種操作情境,隨著開發環境不同可以有不同類型的操作設計。

其實兩個範例不管是不是有 Vue 還是純 JS 設計, API 跟 utils 裡面的 axios 都是不變的,代表這層 Data Layout 已經完全抽離到正常的商業邏輯上,當你要取用的時候,完全不用顧慮到下列 API 到底是打 GET還是用 POST,只要你呼叫對 method 跟帶入正確的 參數,就可以回傳一個 Promise 可以使用,呼叫多個 API 也不怕有問題,大大減少錯誤的開銷,未來即便是修正了一整塊的 API 區域也只需要把 API 這塊給維護完成就可以輕鬆搞定。

我在自己的專案上,利用這個方式舊版跟新版的 API 分離,再轉接的過程中把 舊版 API 的 TOKEN 給拿起來登入去要回 LIST,最後去新版的 API 實作,等到後端的 API 完全轉移之後,也只需要把舊版的 API 給刪除,替換掉新的,其他頁面根本不用去異動,替換成本相當的小。

You may also like