題注:若是喜歡咱們的文章別忘了點擊關注阿里南京技術專刊呦~ 本文轉載自 阿里南京技術專刊-知乎,歡迎大牛小牛投遞阿里南京前端/後端開發等職位,詳見 阿里南京誠邀前端小夥伴加入~。前端
前段時間刷 Twitter 的時候看到大 V 紛紛提到 Apollo,預測它將在 2018 年崛起。正巧碰上有使用 GraphQL 的機會,在大概翻了下 Apollo 的文檔以後,我下定決心在新的前端項目裏嘗試下拋開已經熟悉的 Redux,徹底使用 Apollo 來寫數據層。一個月後的如今,我必須出來好好讚美下這位「太陽神」了。react
轉眼已經 2018 年了,GraphQL 已再也不是個新鮮的名詞了。15 年短暫的掀起一波討論以後,彷佛也沒有聽到多少它的聲音了。然而 Github 在這幾年裏慢慢成熟,Github 也將新版 api 徹底用 GraphQL 實現。在這裏我就不展開討論 GraphQL 的自己了,它讓先後端之間的數據獲取變得更加簡單。redux
提到前端數據管理,最早想到的就是 Redux,我想不少人都體驗過對 Redux 從陌生到熟悉的各個階段,大體應該是這樣的:後端
爲何累了呢?由於 Flux 的單向數據流對你來講已經再也不新鮮了。大部分時候,store 裏存放的都是從後端請求來的數據,對於它們而言,怎麼樣作 dispatch 和 reduce 其實並非關鍵,反卻是怎麼設計 store 值得考慮。api
讓咱們直接以一個真實的場景做爲例子吧:promise
這是一個很常見的評論列表,拿到需求後咱們就開始寫咱們的 <Comments />
組件了,在 Redux 的範式下,咱們不免要按照這個邏輯來寫:緩存
Comments
的 didMount
裏,dispatch
一個獲取數據的 action
,在這個 fetch action
內發送請求。爲了作 loading,咱們極可能要再 dispatch 一個 action 去通知 redux 咱們發起了一個請求。Comments
內咱們收到了 props 傳來的數據,正式開始渲染咱們大量的工做花費在瞭如何獲取數據上。而咱們面臨的挑戰又是什麼呢?看幾個產品經理們可能會提的需求bash
簡單,從新請求一遍整個列表接口就行了!通常而言確實足夠了,不過要求高的產品可能會要求你作」樂觀「更新來讓體驗更好。這也沒什麼問題,加個 reducer
就是。markdown
首先你會想,後端大哥能不能把這些字段都幫我加在評論的接口數據裏,他堅決果斷的拒絕了你,拿出一個 commonUser 的接口讓你本身去調。細一想用戶數據量不小,評論裏也有大量的相同用戶,不放在列表裏也確實合理。心一橫,乾脆把前端這裏的數據結構所有 normalize 化,按用戶 id 爲 key 用哈希表來存放數據。也就一個下午,你獲得了一個很是完美的解決方案。數據結構
面對這樣的場景,咱們寫了太多的 命令式 代碼,咱們一步步的描述了怎麼去獲取評論數據,在獲得評論數據後再提取出全部的用戶 id,去重後再次請求獲取全部的用戶數據,等等。咱們還須要考慮緩 normalize, 緩存,樂觀更新等等細節上的問題。而這些,偏偏是 redux 幫不了咱們的。因而咱們會基於 Redux 封裝更強大的庫和框架,但真正 focus 在數據獲取上的好像還真沒看到很是合適的。
那麼在 Apollo 的世界裏是什麼樣的呢?
import { graphql } from 'react-apollo'; const CommentsQuery = gql` query Comments() { comments { id content creator { id name } } } `; export default graphql(CommentsQuery)(Comments); 複製代碼
咱們使用了 graphql
(類比到 redux 中的 connect) 做爲高階組件將一條 GraphQL 的查詢語句綁定到了 Comments 組件上,而後你全部的一切就準備就緒了。這麼簡單麼?是的,咱們再也不須要描述怎麼在 didMount 裏發送請求,怎麼處理請求來的數據。而是委託 Apollo 幫咱們處理這些全部事情,它會稱職的幫咱們在須要的時候發送請求獲取數據,而後將 data 映射到 Comments 的 props 中交給咱們。
不止於此,當咱們作更新操做的時候也會便捷許多。好比修改一條評論。咱們定義一個 graphql 的 mutation 操做:
// ... const updateComment = gql` mutation UpdateComment($id: Int!, $content: String!) { UpdateComment(id: $id, content: $content) { id content gmtModified } } `; class Comments extends React.Component { // ... onUpdateComment(id, content) { this.props.updateComment(id, content); } // ... } export default graphql(updateComment)(graphql(CommentsQuery)(Comments)); 複製代碼
當咱們調用 updateComment 時,你就會神奇的發現,列表中的評論數據自動更新了。這是由於 apollo-client 把數據按照類型自動緩存在了 cache 中,GraphQL 節點返回的任何數據都會自動被用來更新緩存,在 UpdateComment 這個 mutation 中,咱們定義了它的返回值,一條類型爲 Comment 的新修改評論,而且指定了須要接受的字段,content
和 gmtModified
。這樣,apollo-client 就會自動經過 id 和類型去更新緩存中的數據,從而從新渲染咱們的列表。
再看看剩下的需求,咱們須要在鼠標停留在用戶頭像時展開用戶詳情。這個需求下咱們不只僅須要定義咱們須要什麼數據,還會關心「怎麼」獲取數據(在 hover 頭像時發送請求)。Apollo 一樣爲咱們提供了 「命令式」 的支持。
class UserItem extends React.Component { // ... onHover() { const { client, id } = this.props; client.query({ query: UserQuery, variables: { id } }).then(data => { this.setState({ fullUserInfo: data }); }); } } export default withApollo(UserItem); 複製代碼
幸運的是這裏咱們依然不須要本身考慮緩存的問題。得益於 Apollo 全局的數據緩存,當咱們查詢過用戶 A 以後,再次查詢相同 id 的數據會直接命中緩存,apollo-client 會直接 resolve 緩存中的數據,並不發送請求。這時候問題來了,假設我就是想要每次都從新查詢呢?
client.query({ query: UserQuery, variables: { id }, fetchPolicy: 'cache-and-network' }); 複製代碼
Apollo 給咱們提供了不少策略來自定義緩存邏輯,好比默認的 cache-first
(優先使用緩存),這裏的 cache-and-network
(先使用緩存,同時發請求更新),以及 cache-only
和 network-only
。
這些就是 GraphQL 和 Apollo 很吸引個人一些地方。當你開始從 GraphQL 的角度來思考,你更多的關心的是你的業務組件須要什麼數據,而不是怎麼一步步的得到它。而剩下的大部分業務場景,均可以經過前端的數據類型推導和緩存自動解決掉。固然,篇幅有限,還有不少優雅的地方來不及說起,好比分頁,直接操做緩存達到樂觀更新,輪詢查詢,以及數據訂閱等等。若是有機會的話咱們能夠繼續深刻探討。
看到這裏,你可能會以爲 「GraphQL 很酷,Apollo 也很酷,可是個人後端是 REST,目前是與他們無緣了」。其實否則,從 Apollo Client 的 2.0 版本開始引入了 Apollo Link,理論上來講咱們能夠經過 GraphQL 從任何類型的數據源獲取數據。
「經過 GraphQL「 意味着咱們可使用書寫 GraphQL 的查詢語句來獲取不管是 rest api 或是 client state 中的數據,這樣 Apollo Client 能夠替咱們管理應用中全部的數據,包括緩存和數據拼接。
const MIXED_QUERY = gql` query UserInfo() { // graphql endpoint currentUser { id name } // client state browserInfo @client { platform } // rest api messages @rest(route: '/user/messages') @type(type: '[Message]') { title } } `; 複製代碼
在這樣一個 Query 查詢中,咱們使用 GraphQL 的 directive 拼接了來自於 GraphQL,rest,client state 中的數據,將它們抽象在一塊兒維護。與之相似的,咱們還能夠封裝相應的 mutation 實現。
以上大概就是我這段時間使用 Apollo 和 GraphQL 的一些淺淺的實踐。雖然接觸的不深,但我能夠感覺到 Thinking in GraphQL 爲前端帶來的更優雅的解決方式,和 Apollo Client 這樣一個完整的前端數據層解決方案的高效。我相信在 2018 年,它們會迎來更大的增加,甚至有代替 redux 成爲通用數據管理方案的可能。
Apollo 相關的社區也比較活躍,在 dev-blog.apollodata.com 上也常常發表一些頗有參考價值的文章,有興趣能夠隨便看看~