你們都知道咱們目前應用的統一技術架構是先後端分離的形式,可是在實際開發中,二者之間在數據結構、數據交互方案、業務邏輯等等方面老是存在各類各樣的矛盾,彷佛必定要有一方在設計或實現上向另外一方妥協。好比一個頁面在初始化時,前端不得不調用多個接口來獲取多個微服務提供的業務數據,由於後端但願在微服務之間減小互相調用達到彼此解耦的狀態;再好比一個接口既有WEB端請求的需求也有APP端請求的需求,不可避免的就會出現一大堆冗餘數據是咱們不想看見的。前端
因此你們或多或少應該都思考過中間層的問題:也許咱們能夠把這些先後端之間的矛盾化解在中間層裏。web
在思考這個問題的時候,咱們發現了一種全新的定義API的方式——GraphQL。傳統的web API大概就是REST(表述性狀態傳遞,Representational State Transfer),發送請求到一個特定的URL,而後你會收到各類結果,好比HTML,XML,JSON,PDF,JPEG…等等這些web能夠理解的任一格式。可是GraphQl提出一種新的理念,若是前端開發能夠自由定義請求和響應,若是前端開發能夠精確地指定我要從API獲取那些數據,是否是後端的接口設計只須要考慮業務模型作最正宗的RESTFUL API了呢?後端
這樣理想化的只能出如今想象中的解決方案,不管是否能做爲適合咱們技術架構的中間層方案,彷佛都值得咱們深刻了解一下。api
「一切皆是圖:使用 GraphQL,你能夠將你全部的業務建模爲圖」數組
這是官方的自身定義,其實GraphQL這個名字,Graph + Query Language,就代表了它的設計初衷是想要用相似圖的方式表示數據。接下來會重點介紹一下GraphQL的「繪圖」方式,請你們不要糾結於下面的語法,由於本文的目的不是讓你們徹底掌握GraphQL的語法,而是理解它的運行機制以及核心概念。瀏覽器
先來看一組常規的REST接口是如何設計的:緩存
GET /api/v1/products/ GET /api/v1/product/:id/ POST /api/v1/product/ DELETE /api/v1/product/:id/ PATCH /api/v1/product/:id/
假設咱們要在前端展現一個商品庫,以上五個接口就是很是標準的請求地址定義,後臺返回咱們提早商議好的數據類型便可,那麼GraphQL如何「繪製」這些接口呢?服務器
query { product(): [Product!]! product(id: Int): Product! } mutation { createProduct(): Product! updateProduct(id: Int): Product! deleteProduct(id: Int): Product! }
第一次接觸看起來有點陌生,好的,咱們來解釋兩個概念:Schema 和 Tpye Modifier網絡
1.Schema 數據結構
Schema是用來描述接口獲取數據的邏輯的,咱們能夠簡單的理解爲REST架構中每一個獨立資源的URI,只不過在GraphQL中,是經過<查詢>來描述資源的獲取方式,Schema就是多個<查詢>組成的一張表。(本文把<查詢>作了特殊標記,但願明確它是屬於GraphQL的一種特殊行爲,區別於其餘語義)
上面的商品庫案例中,兩個特殊標記query和mutation就是<查詢>的其中兩種類型,也是咱們在項目中最經常使用的兩種,還有第三種類型是爲了長連接提出的subscription類型。三種類型對應不一樣的<查詢>場景:
很明顯,獲取商品庫列表和單個商品詳情屬於query類型,新增、修改、刪除商品屬於mutation類型,咱們好像讀懂了一點上面的案例。第三種subscription類型是經過聲明式的定義創建一個長連接,來獲取服務端的推送消息,這裏不展開。
2.Tpye Modifier
在GraphQL中有兩種類型修飾符,分別是List和Required,他們在案例中體現分別是[Product]和Product!,[Product]表示它的類型是一個元素爲Product的數組,Product!表示該字段爲必須返回的字段。細心地小夥伴會發現案例中二者能夠結合使用,那麼關於 [Product]! 和 [Product!] 和 [Product!]! (注意 ! 的位置)的區別爲:
瞭解了Schema 和 Tpye Modifier以後,再來看一下GraphQL對商品庫幾個接口的定義
query { products(): [Product!]! product(id: Int): Product! } mutation { createProduct(): Product! updateProduct(id: Int): Product! deleteProduct(id: Int): Product! }
有沒有以爲清晰簡潔一目瞭然呢?可是這隻完成了一半的工做,別忘了咱們真正要作的是自定義返回數據啊!上面的<查詢>中,到底會返回什麼樣的數據呢?咱們須要再瞭解兩個新的概念:Resolver 和 Type。
3.Resolver (解析函數)和 Type
咱們在<查詢>中聲明瞭products(): [Product!]!,那麼這個<查詢>對應的解析函數名必然叫products,這樣才能對應起來,這個解析函數的聲明過程以下:
Query { products { id name price classify { id name } } }
這裏就應用了兩種簡單的Type,標量類型和對象類型,很明顯id、name、price就屬於標量類型,classify就屬於對象類型,classify中的id和name屬於標量類型,這些應該很好理解。
GraphQL在解析這段查詢語句時會按以下步驟:
能夠發現,GraphQL大致的解析流程就是遇到一個<查詢>以後,嘗試使用它的解析函數取值,以後再對返回值進行解析,這個過程是遞歸的,直到所解析的類型是標量類型爲止,整個過程咱們能夠把它想象成一個很長的解析鏈。
以上只是對GraphQL的運行機制以及核心概念進行最簡單的介紹,若是你們感興趣的話,能夠到官網學習更詳細的API。
1.請求冗餘和數據冗餘:
文章開頭提出的兩種矛盾,就是請求和數據的冗餘問題,均可以經過GraphQL解決。
2.靈活且強類型的Schema:
GraphQL的<查詢>具備明確聲明特性是它最顯著地特色。查詢和返回數據有個明肯定義不僅是爲了與API和實現方面保持一致,同時能夠做爲接口校驗的一種方式。
3.下降接口變更的維護成本:
REST中,一旦要改動API,無論是增刪值域,改變值域範圍,仍是增減API數量,改變API url,都很容易變成傷筋動骨的行爲。GraphQL就輕鬆多了。GraphQL的Service,API endpoint極可能就只有一個,根本不太會有改動URL path的狀況。至始至終,數據的請求方都只須要說明本身須要什麼內容,而不須要關心後端的任何表述和實現。數據提供方,好比server,只要提供的數據是請求方的母集,不論它們各自怎麼變,都不須要由於對方牽一髮而動全身。
4.提高應用程序性能:
服務器雖然一樣須要調用與客戶端相同的服務和REST API。可是大多數數據是在同一數據中心內的服務器之間流動。這些服務器到服務器之間的調用延遲很是低,並且帶寬很是高,與瀏覽器的網絡調用相比,性能提高是很是顯著的。
目前GraphQL的應用已經日益普遍了
總的來講GraphQL最大的優點,就是它可以大大提升開發者的效率,並且最大化地簡化了前端的數據層的複雜性,而且使先後端對數據的組織觀點一致。只是真正要用的話,須要考慮遷移成本、性能、緩存等等太多方面的要求,但願有一天咱們能夠真正去思考如何應用GraphQL。
參考資料:
1.官方文檔:https://graphql.cn/
2.網飛的實戰經驗:https://baijiahao.baidu.com/s...