Relay - React數據獲取框架
React在前端領域開啟了一個新的時代。隨著非死book發布并開源React,它迅速成為 大量技術公司 運用在生產環境中的一款流行的庫。在本文中,我們將會討論的是一個全新的React附屬框架——Relay。
Statement
原文地址:http://www.sitepoint.com/react-data-fetching-with-relay/
React中數據獲取存在的問題
由于React正在變得越發流行,使用React構建的項目的規模和復雜度也隨之增加。 由于React只是一個視圖層的庫,這使得某些團隊需要在不同的基礎設施上構建項目時會面臨很多未知的問題和挑戰。 基于這些痛點,非死book也前瞻性的給予了很多技術支持和指導。
Flux
對于使用React的開發者而言,早期的主要痛點之一是事件處理。為了解決這個問題,非死book發布了Flux, 它是一種基于單向數據流思想來解決React中事件處理問題的設計模式(不是一個框架)。
我假設你已經對Flux有所了解了,因此本文不會具體的去討論Flux。假如你對Flux并不了解,可以參考下面兩篇文章:
Flux將React生態系統帶到了一個更高的層級。即使這樣,當開發者開始使用并熟悉Flux時,一些新的問題又出現了。 Flux適合用于管理作為應用程序狀態的數據,但是如何將初始狀態傳遞進應用程序成為了又一個問題之源。
圍繞著Flux的數據初始化面臨著許多的挑戰。在Store中向服務器發起請求并傳遞給自身?在dispatcher中使用rehydrate方法? 在服務器上調用一組actions來傳遞到store?對于同構應用而言,如何在返回響應前完成數據的異步加載?
什么是Relay
Relay是非死book發布的一個全新的React數據獲取框架。 Relay致力于為所有上述這些數據獲取問題提供清晰的解決方案。
Relay主要有以下幾個特點:
- 描述性 Declarative:這也是React的主要特點之一,對于數據依賴,Relay使用了描述性的代碼風格定義, 這非常類似于在React中定義視圖層組件的方式。這也是和傳統的命令式的數據獲取API主要不同的地方。
- 托管 Colocation:數據依賴的定義始終伴隨著組件的定義。這使得能夠非常簡單的推出UI組件需要渲染哪些數據。 這使得解決項目中的代碼問題變得非常簡單。只需檢查一個文件中包含的React組件定義,就能立刻知道函數所需要的數據是什么。
- 變種 Mutations:它使得無縫的數據修改稱為可能,也就是說React視圖層會訂閱數據,并且能夠會將修改傳播到數據持久化層。
Relay vs Flux
Flux是一個抽象的概念,而Relay是基于這個抽象概念的實現。Relay是建立在Flux的概念基礎上, 因此也有和Flux中相同的概念,例如dispatcher,actions,和stores,但它們代表的含義有所不同。 Relay有一個新的叫做“高階組件”的概念,我們在本文的后面會繼續討論這個概念。
到現階段為此,還不清楚Relay是否會替代(或并存)現存的Flux實現。 例如, Redux ,它是一個非常流行的Flux實現, 同樣也使用了“高階組件”的概念。如果你嘗試同時使用Redux和Relay,會遇到具體由哪個框架綁定到UI組件上的沖突。 目前,你可以參與 Redux和Relay關系的討論 。
高階組件
高級組件(Higher Order Compoenents, 簡稱HOC)的定義采用和普通的React組件相同的方式。 HOC組件會包裹將UI組件作為孩子包裹起來(稱UI組件為子UI組件)。HOC組件會執行相關查詢, 然后渲染子UI組件,并將查詢數據作為props傳遞進UI組件。
現在Flux數據流由HOC組件管理,之后將會扮演dispatcher的角色。它具有一個類似于setQueryParams()的方法, 可以將其看作為Flux中的action。調用setQueryParams()將會觸發Flux數據流。 定義在HOC組件中的查詢被更新,新的數據被獲取,并被持久化在HOC組件中。 通過持久化這些數據HOC組件也扮演者Flux中Store的角色。
下面來舉個簡單的例子來說明上面的描述:假設有一個ProfilePicture組件和一個對應的HOC組件。 其中ProfilePicture組件可以用來在我們的整個項目中渲染用戶的頭像。相應的,我們需要獲取數據來展示用戶頭像。 因此我們可以借助Relay來創建一個HOC組件用于從數據庫中查詢用戶的頭像信息。 最后,由HOC組件將數據傳遞給子UI組件(即ProfilePicture組件)。
class ProfilePicture extends React.Component { // A standard Profile Picture component } // HOC: it fetches the data to pass as props to ProfilePicture module.exports = Relay.createContainer(ProfilePicture, { fragment: { user: () => Realy.QL ` fragment on User { profilePicture(size: $size) { uri, }, } `, } });
然后,我們的ProfilePicture組件會通過傳遞進來的props獲得一些新的本地函數。 這就是Relay如何觸發Flux數據流的基本原理。組件調用這些Relay的prop函數,就類似于調用Flux的action。 這可以讓Relay去獲取最新請求的數據,一旦數據獲取完成便將其內部的store作為Props傳遞給HOC組件的子視圖組件。
GraphQL
上面的代碼中可能有一部分比較陌生的地方,尤其是這部分代碼:
Relay.QL` fragment on User { profilePicture(size: $size) { uri, }, } `,
在Relay背后絕大部分神奇的地方都是由GraphQL驅動的。GraphQL是非死book開發的一種全新的的查詢語言, 尤其擅長于查詢圖結構的數據。深入的討論GraphQL不屬于本文的范疇,你可以參考以下幾篇文章來了解GraphQL:
現有的項目并不能直接與GraphQL集成,需要做相關的配置工作。首先你需要做的是:
- 創建GraphQL的Schema
- 創建一個GraphQL服務器
值得考慮的的是,對于已有的項目,如果想要改造項目以使用GraphQL模式可能會涉及到大量的修改工作, 你需要將現有服務器配置并修改為GraphQL友好的。因此,推薦你在啟動一個新項目時嘗試使用Relay, 為此,非死book還提供了一個 Relay Starter Kit 來幫助你快速的使用Relay和GraphQL啟動一個新的項目。
沒有GraphQL的Relay
由于設置GraphQL涉及到一些額外工作,因此Relay并不適合在現有項目中使用。幸運的是,受到Relay的啟發, 有一個叫做react-transmit的庫可以更好的適應現有項目。它是一個開源項目, 其宣傳語就是“一個受Relay啟發的,使用Promise代替GraphQL的開源庫”。
我們可以用react-transmit來重寫上面的例子,代碼如下:
// Import Transmit import Transmit from "react-transmit"; class ProfilePicture extends React.Component { // A standard Profile Picture component } // This is our Higher Order Component. It fetches the data to pass // as props to Profile Picture Transmit.createContainer(ProfilePicture, { fragments: { user: (userId) => { return new Promise(function(resolve, reject) { // Do some Ajax here and resolve the promise }); } }, });
使用react-transmit的例子看起來和Relay的例子非常的像。但是,代碼中的user部分 現在返回的是Promise,而不是GraphQL查詢。
Relay的近況
非死book已經發布并開源了Relay的技術預覽版。 在它的代碼庫中有一些非常不錯的例子展示了如何使用Relay,并且有一個非常詳盡的文檔。
此刻對于Relay是否適合同構App還沒有定論,因為暫時還沒有方法能夠告訴Relay,讓它在渲染子視圖之前, 先等待所有的數據依賴被加載完成,因此在服務器端需要做些事情。加入你對這個話題感興趣,你可以參與 有關 Relay如何在服務端工作 的討論。 在問題還沒有解決之前,你可以嘗試使用react-transmit來應對相關問題。
至于Relay的未來,它的路線圖展示了它未來的幾個關鍵特性:
- 適用于其他存儲類型的是配置,不僅僅是圖結構的數據。
- 更好的同構支持 ,正如前面所提到的。
總結
在本文中,我們討論一個新的被稱為Relay的React附屬框架。React基于和Flux同樣的概念而構建, 并且由GraphQL驅動。正如我提到的,Relay可能并不適合用于一些現有項目。但是,這個框架非常的新, 期望它能夠有越來越好的版本發布。