前端開發中的時候,很容易把 API 事件寫在同一個 component 裡面,大部分人會進行封裝,讓傳送 API 的方法變得更簡單
先前碰到同事裡用前端框架系統 Vue 的時候,則是把 API 放到 Vuex 裡面,之前看過 React 裡面,如果上下層傳遞有點過於遙遠或是複雜,通常會用 State 來管控, Vue 使用 Vuex ,React 則是用 redux 來表達貫穿這個元件的參數。
不過正如上面說的 Vuex 跟 redux 都是用來放狀態的,如果拿來放其他東西,並不是說不可以,只是如果在物件導向的裡面中,這樣就違反了 單一功能原則的項目,狀態管理就不再是狀態管理。
自從看了一點 Clean Code的書之後,開始針對 Clean Architecture 很感興趣,原本我主要寫後端跟一點點的前端,開始把後端架構邏輯設計延伸到了前端,開始修正前端同事的程式碼。
Clean Architecture:

回到正題 要實現乾淨的架設設計,首先先把專案中的 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 給刪除,替換掉新的,其他頁面根本不用去異動,替換成本相當的小。