GraphQL 和 Relay
本節概述 Relay 在 GraphQL、React 和堆疊的其他部分中的定位。不用擔心要了解每個細節,試著理解大意,然後繼續到下一節開始使用程式碼。在整個教學的範例中,會逐步詳細說明更多細節。
GraphQL 是一種用於在伺服器上查詢和修改資料的語言。GraphQL 的獨特之處在於,它不是擁有一組固定的 API 端點,而是讓伺服器提供一組選項,讓客戶端可以要求任何所需的資料組合。這讓前端開發人員能夠更快地行動,因為當資料需求變更時,不需要撰寫和部署新的端點。這也意味著當發布新版本的客戶端時,它僅能要求它所需要的資料,而不會有額外的欄位遺留下來,以與舊版本相容。
GraphQL 提供了一個統一的介面,用於跨任何類型的後端查詢資料。無論您的資料位於關聯式 SQL 資料庫、圖形導向的資料庫,還是微服務的集合中,GraphQL 伺服器都可以從多個後端收集資料,並將其以單一回應傳送到客戶端,這比從客戶端向每個服務發出單獨的查詢更有效率。
在傳統的 HTTP API 中,每個 URL 都會回應一組固定的資訊
Request:
GET /person?id=24601
Response:
{"id": "24601", "name": "Jean Valjean", "age": 64, "occupation": "Mayor"}
在 GraphQL 中,客戶端會要求它想要的特定資訊,而伺服器則會回應所要求的資訊
Request:
query {
person(id: "24601") {
name
occupation
}
}
Response:
{
"person": {
"name": "Jean Valjean",
"occupation": "Mayor"
}
}
請注意,回應中僅包含客戶端所要求的特定欄位。
顧名思義,GraphQL 將資料組織成一個圖形。該圖形由節點(如物件或記錄)和邊緣(從一個節點指向另一個節點的指標)組成
GraphQL 可讓您追蹤從一個節點到另一個節點的邊緣,並要求每個您訪問的節點的資訊。例如,在這裡,我們從一個人轉到他們的城市,並取得有關該城市的資訊
- 請求
- 回應
query {
person(id: "24601") {
name
occupation
location {
name
population
}
}
}
{
"person": {
"name": "Jean Valjean",
"occupation": "Mayor",
"location": {
"name": "Montreuil-sur-Mer",
"population": 1935
}
}
}
這意味著我們可以在一個查詢中檢索整個物件的資訊,換句話說,您可以有效率地在單一請求中取得畫面所需的所有資料,而不是一個接一個地發送多個請求。但您無需為 UI 中的每個畫面撰寫和維護單獨的端點即可達成此目的。
相反地,您的 GraphQL 伺服器會提供一個結構描述,其中描述有哪些類型的節點、它們如何連接,以及每個節點包含哪些資訊。然後,您可以從這個結構描述中挑選並選擇您想要的資訊。
本教學中的範例應用程式是一個新聞摘要應用程式,因此其結構描述包含以下類型:
Story
,表示新聞摘要中的文章 — 它有諸如標題、圖片,以及指向發文者或組織的邊緣等欄位Person
,包含姓名、電子郵件和朋友列表(指向其他「人員」的邊緣)等資訊。Viewer
,代表正在查看應用程式的人員,並包含他們的新聞摘要文章列表等資訊Image
,包含圖片本身的 URL 以及alt
文字說明。
GraphQL 語言包含一個類型系統和一種用於指定結構描述的語言。以下是我們的範例應用程式的結構描述定義片段 — 不用擔心每個細節,這只是讓您了解大致概念
// A newsfeed story. It has fields, some of which are scalars (e.g. strings
// and numbers) and some that are edges that point to other nodes in the graph,
// such as the 'thumbnail' and 'poster' fields:
type Story {
id: ID!
category: Category
title: String
summary: String
thumbnail: Image
poster: Actor
}
// An Actor is an entity that can do something on the site. This is an
// interface that multiple different types can implement, in this case
// Person and Organization:
interface Actor {
id: ID!
name: String
profilePicture: Image
}
// This is a specific type that implements that interface:
type Person implements Actor {
id: ID!
name: String
email: String
profilePicture: Image
location: Location
}
// The schema also lets you define enums, such as the category
// of a newsfeed story:
enum Category {
EDUCATION
NEWS
COOKING
}
除了查詢之外,GraphQL 還可讓您發送變更,要求伺服器更新其資料。如果查詢類似於 HTTP GET 請求,那麼變更就相當於 POST 請求。與 POST 一樣,它們可讓伺服器使用更新的資料回應。GraphQL 還具有訂閱,可實現即時更新的開啟連線。
(GraphQL 通常透過 HTTP 實作,因此查詢和變更不僅類似於 GET 和 POST,也可能以這種方式發送。)
現在我們已經討論了 GraphQL,接下來讓我們來談談 Relay。它有一些不同的部分和元件,我們將在深入研究程式碼之前簡要地介紹它們。
Relay 是一個以 GraphQL 為中心的客戶端資料管理程式庫,但它以非常特定的方式使用它,從中獲得最大的優勢。
為了獲得最佳效能,您會希望應用程式在每個畫面或頁面的開頭發出單一請求,而不是讓個別元件發出自己的請求。但是,這樣做的問題在於,它將元件和畫面耦合在一起,產生重大的維護問題:如果您需要在特定元件中新增一些額外資料,則必須找到使用該元件的每個畫面,並將新欄位新增到該畫面的查詢中。另一方面,如果您不再需要特定欄位,則必須再次從每個查詢中刪除該欄位 — 但這次,您能確定該欄位不是仍然被其他其他元件使用嗎?維護這些大型的畫面範圍查詢會變得非常困難。
Relay 的獨特優勢在於,透過讓每個元件在本機宣告自己的資料需求,然後將這些需求拼接成更大的查詢,來避免這種權衡。這樣,您就能同時獲得效能和可維護性。
Relay 使用一個編譯器來實現此目的,該編譯器會掃描您的 JavaScript 程式碼中的 GraphQL 片段,然後將這些片段拼接成完整的查詢。
除了編譯器之外,Relay 還具有執行階段程式碼,用於管理 GraphQL 的提取和處理。它會維護所有已檢索資料的本機快取(稱為儲存),並將屬於它的資料銷售給每個元件
擁有集中式儲存的優點在於,當它更新時,可讓您保持資料的一致性。例如,如果您的 UI 提供使用者編輯其名稱的方式,那麼您可以在單一位置進行更新,並且每個顯示該人員名稱的元件都會看到新資訊,即使它們位於不同的畫面上,因此使用不同的查詢來初始檢索資料。這是因為 Relay 在資料進入時會標準化資料,這表示它會將它所看到的單一圖形節點的所有資料合併到一個位置,因此它不會有多個相同節點的複本。
實際上,Relay 不僅僅查詢資料,它還提供查詢和更新的整個生命週期,包括對樂觀更新和回滾的支援。您可以分頁、重新整理資料 — 建立 UI 所需的所有基本操作。每當儲存中的資料更新時,Relay 會有效率地重新轉譯僅顯示該特定資料的元件。
摘要
GraphQL 是一種用於將資料建模為圖形並從伺服器查詢該資料(以及更新資料)的語言。Relay 是一個基於 React 的 GraphQL 客戶端程式庫,可讓您從與每個 React 元件共置的個別片段建構查詢。查詢資料後,Relay 會維護一致性,並在資料更新時重新轉譯元件。