隨着先後端分離,開發的門檻下降了,咱們再也不要求團隊中的每一個開發都是全棧工程師,這樣更容易找到項目的合適人選。團隊也劃分紅了前端和後端兩個團隊。前端負責消費 API 並展現頁面,後端負責提供 API。這兩個團隊能夠並行開發互不影響,大大提高了效率。雖然先後端分離解決了不少問題,但同時也帶來了新的困擾。前端
先後端成爲兩個獨立團隊以後,協做的問題便隨之而來。經過什麼來協做呢?契約。簡單來講,就是預先定義好精準的接口,好比接口的 URL,包含哪些參數、返回值,每一個值的類型,是否爲空等等。定義好以後,先後端就按照契約進行開發。可是在實際場景中,卻常常出現問題。git
舉個真實的例子。有一次,後端在重構時修改了一個字段名,同時也修改了契約測試,可是卻忘了告訴前端。因爲這個頁面的使用頻率不高,前端也工做在別的地方,所以那個頁面掛了許久都沒有人發現。這些隱藏 Bug 會給咱們的應用帶來隱患,同時也會增長開發的負擔。github
在實際開發過程當中,保證人人都遵照契約是一個很困難的事情,由於人均可能會犯錯。typescript
即使團隊中全部人都能嚴格遵循契約,但集成 API 仍舊是個苦力活。我須要定義請求的 URL、Method、參數、參數類型以及返回值類型等等。因而項目中就充斥着下面這樣的模板代碼:json
interface ICreateBookRequestData { bookId: string; category: string; date: string; createdBy: string; } interface IBook { id: string; author: string; name: string; price: string; publishDate: string; publishVendor?: string; } export const createBook = createRequestAction<ICreateBookRequestData, IBook>( "@@books/createBook", (data) => ({ url: "/books/book", method: "PUT", data, }), );
在多人協做時,爲了減小衝突,咱們一般會按照業務場景,將請求相關的代碼存儲到不一樣文件。好比 login.api.ts
存放登陸相關的請求代碼,account.api.ts
中存放帳戶相關的請求代碼。可是這會形成另外一個問題,就是類型的重複定義和定義不一致的問題。後端
在上面的代碼中,咱們定義了請求響應數據的類型:IBook
。對於後端來講,這個數據類型是可複用的,其餘接口也可使用它。可是對於前端來講,很難肯定這個數據類型在哪些地方被使用了。所以在不一樣的文件中,可能會重複定義相同的類型。因爲不一樣的人對同一個數據的類型的理解可能不一致,還會形成定義不一致的問題。好比在 A 文件的 IBook
中咱們定義 publishVendor
是一個可選的屬性,而在 B 文件中咱們可能再次定義 IBook
並把 publishVendor
定義爲一個必需的屬性。以下所示:api
interface IBook { id: string; author: string; name: string; price: string; publishDate: string; publishVendor: string; }
爲了解決上面的問題,咱們實現了一個自動化工具,將割裂的前端和後端從新鏈接起來。簡單來講,就是經過 Swagger JSON 自動生成調用 API 所需的代碼以及類型定義。服務器
OpenAPI 規範(之前稱爲 Swagger 規範)爲 RESTful API 定義了一個與語言無關的標準接口,容許人和計算機發現和理解服務的功能,而無需訪問源代碼、文檔或開發者工具。Swagger 是一套圍繞 OpenAPI 規範構建的開源工具,能夠幫助咱們生成、描述、調用和可視化 RESTful 風格的服務。
有了自動化工具以後,只須要在終端中執行一行命令,就能馬上生成項目中全部 API 相關的代碼以及類型定義。這樣就不用再寫模板代碼了,節省了不少時間。同時,因爲全部代碼都是經過 Swagger JSON 生成的,當接口發生變更時,咱們不用再去查看文檔或者詢問後端修改了什麼,只須要經過命令就能知道哪些接口發生了變化並自動更新對應的前端代碼。由於全部代碼都是自動生成的,重複定義的問題也就不存在了。對於簡單業務場景來講,集成 API 就是一行代碼的事:前後端分離
// getBooksUsingGET 方法由工具自動生成, useTempData 會發起 HTTP 請求並返回響應數據 const [books] = useTempData(getBooksUsingGET, { bookType }, [bookType]); // 拿到 books 數據,渲染 UI
當我實現完這個工具的第一個可用版本,準備在項目中推行時,卻發現還有一些問題亟待解決:工具
Q: 如何保證生成代碼的一致性?
A: 每次運行命令都會從遠程服務器上去獲取 Swagger JSON,以保證數據來源的一致性。生成新的代碼以後覆蓋以前的文件便可。這樣就能保證每一個人生成的代碼與當前服務器上的 API 是一一對應的。不過須要注意的是,生成的文件不能手動修改,不然修改最終會被覆蓋。
Q: 如何快速得知 API 的變化?
A: 跟 package-lock.json
同樣,咱們會對生成的代碼進行排序,以減小生成文件的變化。從新運行命令以後,經過 git diff
就能準確得知 API 的變化。
Q: 如何進行多人協做?
A: 若是後端修改了一個字段名,可能會致使前端全部用到這個字段的地方都發生編譯錯誤。這時若是你們都去修改編譯問題,不只可能產生衝突,還會形成時間的浪費。雖然這個問題在沒有自動化工具以前同樣存在,但仍然須要解決。好在這個問題發生的頻率不高,咱們能夠和項目成員約定:若是有人正好在作這個功能,那麼就由他來修改,不然就由指定的人去協調安排。經過自動化工具,能夠更快地完成修改,從而減小阻塞別人工做的時間。
Q: 當後端進度落後於前端時,如何保證先有 Swagger 定義?
A: 因爲先後端是兩個獨立的團隊,因此進度也經常不一樣。後端可能沒法先於前端實現好 API,甚至沒法和前端同時開始去作一個功能。而這套方案依賴於 Swagger 定義,必須先有 Swagger 定義才能生成代碼。若是前端要先於後端完成某個功能,必須先和後端商定好 API Schema 再進行開發。定義好 API Schema 以後,隨之更新 Swagger 定義便可(後端無需實現具體功能)。
自動化工具爲先後端搭建了一座橋樑,當後端發生變更時,前端也能及時得知並作出相應修改。不再用擔憂後端悄悄改接口了!到目前爲止,自動化工具已經爲咱們項目生成了上萬行代碼。不只提高了你們的效率,也減小了由於不遵循契約帶來的隱藏 Bug。前端終於不用寫大量模板代碼了,集成 API 也變成了一件很容易的事情。
若是對代碼實現感興趣,能夠移步這裏:ts-codegen,或者看看我以前寫的這篇文章:基於 React 和 Redux 的 API 集成解決方案。