成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

GraphQL and Relay 淺析

Luosunce / 833人閱讀

摘要:包括什么把關(guān)于數(shù)據(jù)獲取的事情都接管過來,比如說請(qǐng)求異常,,請(qǐng)求排隊(duì),,獲取分頁數(shù)據(jù)。的聲明式數(shù)據(jù)獲取是按組織的,最好的方式也是把需要的數(shù)據(jù)寫在。另外,通過聲明式數(shù)據(jù)獲取還可以更好的對(duì)組件約束,只能獲取它聲明的數(shù)據(jù),并且也可以做些驗(yàn)證。

Facebook 在去年夏天公布了 GraphQL,就像往前端深潭砸下了一顆巨石,人們都被水聲吸引到了湖邊,觀望是否會(huì)出現(xiàn)什么,有些人期待,有些人猜疑。過了半年多,社區(qū)已經(jīng)慢慢的摸清這個(gè)石頭的材質(zhì),本文希望在你入門 GraphQL 和 Relay 的過程中能幫你清除一些障礙。

GraphQL

GraphQL 是在 Facebook 內(nèi)部應(yīng)用多年的一套數(shù)據(jù)查詢語言和 runtime。
初次入門者建議先把官網(wǎng)的資料都讀一遍,難度不大(specification 和 API 可以后面再看)。

GraphQL 包括什么

類型系統(tǒng) - GraphQL 是強(qiáng)類型語言,強(qiáng)類型雖然寫時(shí)會(huì)稍微累點(diǎn),但就不用寫一堆類型檢測(cè)的代碼了;

驗(yàn)證 - GraphQL 提供機(jī)制對(duì)你的語法和請(qǐng)求做一定層度的校驗(yàn);

introspection - 一個(gè)讓你能通過幾行代碼就能了解整個(gè)資源提供方的細(xì)節(jié)的 API。

GraphQL 優(yōu)勢(shì)

官網(wǎng)已經(jīng)列舉了,我用更簡(jiǎn)練的語言描述下。

GraphQL 與 REST

同類型協(xié)議目前最出名的是 REST,特點(diǎn)是資源可定位,使用 HTTP verbs。REST 具體應(yīng)該怎么寫有很多爭(zhēng)議,但簡(jiǎn)單的例子是沒有爭(zhēng)議的:

GET /users/1

REST 優(yōu)點(diǎn)是簡(jiǎn)單明了,缺點(diǎn)也是太簡(jiǎn)單明了,導(dǎo)致語法可擴(kuò)充性不強(qiáng)。
我們來看看 GraphQL 官網(wǎng)是怎么和 REST 對(duì)比的:

語法靈活

GraphQL 只需要一次請(qǐng)求就能夠獲得你所有想要的資源。這里舉一個(gè)和 REST 對(duì)比的例子 讓大家有直觀的認(rèn)識(shí)。

現(xiàn)在,我想獲取id為1的用戶的名字,年齡和他所有朋友的名字

GraphQL 實(shí)現(xiàn)的方案:

{
  user(id: 1) {
    name
    age
    friends {
      name
    }
  }
}

REST 實(shí)現(xiàn)的方案:

GET /users/1 and GET /users/1/friends  

GET /users/1?include=friends.name

發(fā)現(xiàn)區(qū)別了嗎?用 REST 要不就發(fā)多次請(qǐng)求,要不就得用一個(gè)不方便擴(kuò)展的語法。

沒有冗余

日后擴(kuò)充資源也沒有冗余,你只會(huì)獲得你想要的資源。還是用上面的例子,如果 user 多了個(gè)屬性 gender 會(huì)怎么樣?
在 REST 的方案中,如果客戶端不變,取到的結(jié)果是會(huì)多了 gender 屬性,而在 GraphQL 方案中,客戶端是不會(huì)獲取到 gender 屬性的。

強(qiáng)類型

有 introspection 機(jī)制,代碼即文檔,方便快捷,而不需要去找這個(gè) API 的說明文檔在哪里,看個(gè)例子:

自定義 schema

沒必要像 REST 這樣固定且通用的語法。

其他專有方案(Ad Hoc Endpoints)

和專有方案對(duì)比:

專有方案每個(gè)接口都自己定義獲取數(shù)據(jù),后端代碼不能得到重用;

和 REST 對(duì)比的第二點(diǎn)一樣;

每個(gè)接口的數(shù)據(jù)不能復(fù)用;

對(duì)比其他現(xiàn)有的專有方案,要么沒有強(qiáng)類型,要么沒有 GraphQL 這么昂貴,而且前面3點(diǎn)也還是沒有解決。

與圖數(shù)據(jù)庫的關(guān)系

首先,介紹下什么是圖數(shù)據(jù)庫,可以參考neo4j的介紹,一圖勝千言:


上邊是關(guān)系數(shù)據(jù)庫,下邊是圖數(shù)據(jù)庫。

GraphQL 為什么有 Graph,是因?yàn)樗?query 是以圖的形式來組織的:

user
┖-OWNS-> playlist
         ┖-CONTAINS-> track
                      ┖-LIKED_BY-> users

GraphQL 并不要求后臺(tái)一定要是圖數(shù)據(jù)庫,關(guān)系數(shù)據(jù)庫也可以,它只是一套查詢數(shù)據(jù)的語言而已。

DataLoader

Dataloader 是一個(gè)小工具,幫你把你的請(qǐng)求轉(zhuǎn)成批量請(qǐng)求的形式,和 GraphQL 搭配的也挺好,看個(gè)例子:

query FetchPlaylist {
  playlist(id: "e66637db-13f9-4056-abef-f731f8b1a3c7") {
    id
    name

    tracks {
      id
      title

      viewerHasLiked
    }
  }
}

這個(gè) query 是要獲取某個(gè)用戶的歌單。
注意一個(gè)細(xì)節(jié),這個(gè) query 想獲取每個(gè) track 的一些屬性。我們定義一下 Track 這個(gè)類型:

import {
  GraphQLString,
  GraphQLBoolean,
  GraphQLObjectType
} from "graphql";

export default new GraphQLObjectType({
  name: "Track",
  description: "A Track",
  fields: () => ({
    id: {
      type: GraphQLString,
      resolve: it => it.uuid
    }

    title: { type: GraphQLString },

    viewerHasLiked: {
      type: GraphQLBoolean,
      resolve: (it, _, { rootValue: { ctx: { auth } } }) => (
        (auth.isAuthenticated) ? it.userHasLiked(auth.user) : null
      )
    }
  })
});

resolve 函數(shù)調(diào)用的是后端 API,注意這里的 it 就是 track 的對(duì)象。
我們獲取 viewerHasLiked 這個(gè)屬性需要調(diào)用 it.userHasLiked (auth.user)。那么,我的歌單里有 50 首歌的話,就要調(diào)用 50 次it.userHasLiked(auth.user),這樣訪問數(shù)據(jù)庫的性能是無法接受的。合理的想法是變成批量的。那要怎么做呢?這就是 DataLoader 發(fā)揮作用的時(shí)候了:

import DataLoader from "dataloader";
import BaseModel from "./BaseModel";

const likeLoader = new DataLoader((requests) => {
  // requests is now a an array of [track, user] pairs.
  // Batch-load the results for those requests, reorder them to match
  // the order of requests and return.
})

export default class Track extends BaseModel {
  userHasLiked(user) {
    return likeLoader.load([this, user]);
  }
}

在一個(gè) event loop 里每次調(diào)用 dataloader,dataloader 會(huì)記下你的請(qǐng)求參數(shù),在下次 event loop 的時(shí)候把這么多次的請(qǐng)求參數(shù)變成一個(gè)數(shù)組提供你操作,你就可以拿這個(gè)數(shù)組對(duì)數(shù)據(jù)庫執(zhí)行批量的操作了。而且,它還對(duì)結(jié)果按你的請(qǐng)求參數(shù)進(jìn)行了緩存,是居家必備的殺人利器。

安全性

或許有人有疑問,感覺 GraphQL 把我所擁有的資源全部都暴露了,別人不只一覽全局,而且還能一次過全部拉下來,那還得了?
事實(shí)上,GraphQL 提供的資源不一定要和你數(shù)據(jù)庫一樣,因?yàn)樗皇前缪葜虚g層的角色,雖然也可能很像。所以,你要想好哪些資源可以被看。
至于獲取,其實(shí)看到上面的例子里有這句 auth.isAuthenticated。

可以看到你可以在里面插入權(quán)限限制的。至于獲取資源太多拖垮服務(wù)器?

Jacob Gillespie 提到一些思路:

對(duì)語句做 AST 分析,太復(fù)雜的就拒絕了;

做超時(shí)限制,對(duì)容量也可以做限制;

客戶端記得要做 cache(如 Relay)。

Relay

Relay 是連接 GraphQL 和 React 的一座橋梁。不過,除了讓 React 認(rèn)識(shí) GraphQL 服務(wù)器之外,它還做了什么呢?

建議先把官網(wǎng)的資料都讀一遍,Relay 相對(duì)來說比 GraphQL 復(fù)雜一些,而且文檔并不詳細(xì)(截至截稿時(shí),Relay的版本是 v0.6.1),也缺失了關(guān)于 graphql-relay 庫的詳細(xì)介紹,掃一遍后,結(jié)合本文最后的學(xué)習(xí)資料的代碼加深理解。

Relay 怎么用?

使用 Relay 是要侵入前后端的:

在后端你得通過 graphql-relay-js 讓 GraphQL schema 更適合 Relay;

在前端再通過 react-relay 來配合 React。

Relay 包括什么?

Relay 把關(guān)于數(shù)據(jù)獲取的事情都接管過來,比如說請(qǐng)求異常,loading,請(qǐng)求排隊(duì),cache,獲取分頁數(shù)據(jù)。我這里重點(diǎn)講一下以下幾個(gè)方面:

client-side cache

Relay 獲取數(shù)據(jù)當(dāng)然離不開 cache,可以看到 GraphQL 不再依賴 URL cache,而是按照 Graph 來 cache,最大的保證 cache 沒有冗余,發(fā)最少的請(qǐng)求,我舉一個(gè)例子:

比如下面這個(gè)請(qǐng)求:

query { stories { id, text } }

如果利用 URL 請(qǐng)求(比如說瀏覽器的 cache),那么這個(gè)請(qǐng)求下次確實(shí)命中 cache 了,那么假如我還有一個(gè)請(qǐng)求是:

query { story(id: "123") { id, text } }

看得出,下面這個(gè)請(qǐng)求獲取的數(shù)據(jù)是上面請(qǐng)求的子集,這里有兩個(gè)問題:

如果第一第二兩個(gè)請(qǐng)求獲取的數(shù)據(jù)不一致怎么辦?

本來就是子集,為什么我還要發(fā)請(qǐng)求?

這兩個(gè)想法催生出來了 GraphQL 的解決方案:按照 Graph 來 cache,也就是說子集不需要再發(fā)請(qǐng)求了,當(dāng)然你也可以強(qiáng)制發(fā)請(qǐng)求來更新局部或者整個(gè) cache。

具體做法是通過拍平數(shù)據(jù)結(jié)構(gòu)(類似數(shù)據(jù)庫的幾個(gè)范式)來 cache 整個(gè) Graph。

view 通過訂閱他需要的每個(gè) cache record 來更新,只要其中一個(gè) record 更新了,也只有訂閱了這個(gè) record 的 view 才會(huì)得到更新。

最后,聊到修改,我們可以看到 mutation 有個(gè)反直覺的地方是請(qǐng)求的 query 里包括了需要獲取的數(shù)據(jù)。為什么不直接返回你的修改影響的那些數(shù)據(jù)? 因?yàn)榉?wù)端實(shí)現(xiàn)這個(gè)太復(fù)雜了,有的時(shí)候一個(gè)簡(jiǎn)單的修改會(huì)影響到非常多的后臺(tái)數(shù)據(jù),而很多數(shù)據(jù) view 是不需要知道它變化了。

所以,Relay 團(tuán)隊(duì)最后選擇的方案是,讓客戶端告訴服務(wù)器端你認(rèn)為哪些數(shù)據(jù)你想重新獲取。具體到實(shí)現(xiàn),Relay 采用的方案是獲取 cache 和 fat query 有交集的部分,這樣既更新了 cache,而且不在 cache 里的也不會(huì)獲取。

Relay 的聲明式數(shù)據(jù)獲取

React 是按 Component 組織 view 的,最好的方式也是把 view 需要的數(shù)據(jù)寫在 view。如果用常規(guī)的做法,view 負(fù)責(zé)自己的 Data-fetch,那么,由于 React 是一層一層的往里深入 Component 的,那么也就意味著每一層 Component 都自己發(fā)請(qǐng)求去了,是不可能做到用一個(gè)網(wǎng)絡(luò)請(qǐng)求來獲取所有數(shù)據(jù)的。

所以,Relay 通過抽象出一個(gè) container 的概念,讓每個(gè)模塊提前聲明自己需要的數(shù)據(jù),Relay 會(huì)先遍歷所有 container,組成 query tree,這樣就達(dá)到了只使用一個(gè)網(wǎng)絡(luò)請(qǐng)求的目的。

另外,通過聲明式數(shù)據(jù)獲取還可以更好的對(duì)組件約束,只能獲取它聲明的數(shù)據(jù),并且 Relay 也可以做些驗(yàn)證。

graphql-relay-js

在看一些 React 和 Relay 協(xié)作的例子時(shí),經(jīng)常發(fā)現(xiàn)這個(gè)庫的存在,這個(gè)庫到底是干什么的?

通過查看源碼后發(fā)現(xiàn),里面其實(shí)是各種 helper 方法,負(fù)責(zé)生成一些 GraphQL 的類,為什么需要這樣做?其實(shí),這是因?yàn)?Relay 提供的一些功能(比如 ID handling,分頁)需要 GraphQL 服務(wù)器提供特定的代碼結(jié)構(gòu)。如果你要開發(fā)一個(gè) GraphQL 的前端,就算它基于其他框架,基于其他語言,實(shí)現(xiàn)一個(gè)像 graphql-relay-js 所實(shí)現(xiàn)的 Relay-compliant 的 server 是很有幫助的,比如graphql-go/relay。

babel-relay-plugin

Relay 的 container 依賴的數(shù)據(jù)資源是通過聲明的,但客戶端是不知道后端的數(shù)據(jù)結(jié)構(gòu)的。為了讓客戶端了解整個(gè)后臺(tái)結(jié)構(gòu),就要引入這個(gè) bable 插件,這個(gè)插件通過讀取服務(wù)端的 schema,就可以讓客戶端正確理解它所需要的資源在服務(wù)端是長(zhǎng)什么樣的。

optimistic UI update

我們看下例子:

Loading...
; }} renderFailure={function(error, retry) { return (

{error.message}

); }} />

可以看到在 Relay 里可以很簡(jiǎn)單的處理請(qǐng)求整個(gè)請(qǐng)求過程中的 UI 變化。

總結(jié)

相信閱讀本文的讀者都是對(duì)這兩者有一定興趣的人,但在我上手之后,我的心情是復(fù)雜的。GraphQL 和 Relay 帶來了一些優(yōu)勢(shì),最重要的是可以一次性獲取資源,看上去是未來之路,但這優(yōu)勢(shì)其實(shí)用些不優(yōu)雅的方法來解決也沒什么問題,但為了這些優(yōu)勢(shì)需要編寫大量與業(yè)務(wù)邏輯無關(guān)的代碼,讓我真心憂慮它的路能走多遠(yuǎn),相信看過一個(gè)官方的 TODOList的例子 的入門者很容易就能感覺到。REST 如此簡(jiǎn)單,普及開來尚且用了幾年,復(fù)雜好多倍的 GraphQL 的未來還任重而道遠(yuǎn)。

學(xué)習(xí)資料

GraphQL 和 Relay學(xué)習(xí)資源匯總:這里列舉了比較全的相關(guān)學(xué)習(xí)資源,5顆星。

搭建你的第一個(gè) GraphQL 服務(wù)器:這篇文章從0開始幫你搭建一個(gè) GraphQL,比較淺,3顆星。

relay-starter-kit:這個(gè)例子簡(jiǎn)單的描述了 Relay 和 GraphQL 的關(guān)系,但沒有 mutation,3顆星。

From rest to GraphQL:提到了rootValue,dataloader,講了比較真實(shí)的例子,5顆星。

Relay 官方例子 TODOlist:比較完整的增刪改查的官方例子,5顆星。

Unofficial Relay FAQ:這篇 FAQ 是 Facebook 員工寫的,里面提到 Relay 是要取代 Flux,而且 routing 還在積極修改中。

相關(guān)的庫

server:比如 express-graphql。

ORM:比如 graffiti。

facebook/dataloader。

adrenaline:React bindings for Redux with Relay。

react-router-relay:結(jié)合 react-router,介紹。

graphql-relay-js

babel-relay-plugin

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78869.html

相關(guān)文章

  • Flux再進(jìn)化:Introducing Relay and GraphQL

    摘要:它的設(shè)計(jì)使得即使是大型團(tuán)隊(duì)也能以高度隔離的方式應(yīng)對(duì)功能變更。獲取數(shù)據(jù)數(shù)據(jù)變更性能,都是讓人頭痛的問題。通過維護(hù)組件與數(shù)據(jù)間的依賴在依賴的數(shù)據(jù)就緒前組件不會(huì)被渲染為開發(fā)者提供更加可預(yù)測(cè)的開發(fā)環(huán)境。這杜絕了隱式的數(shù)據(jù)依賴導(dǎo)致的潛在。 關(guān)于Relay與GraphQL的介紹 原文:Introducing Relay and GraphQL 視頻地址(強(qiáng)烈建議觀看):https://www.y...

    cncoder 評(píng)論0 收藏0
  • 簡(jiǎn)單暴力!21 分鐘學(xué)會(huì) apollo-client + redux

    摘要:閱讀過程中如果產(chǎn)生任何不適,請(qǐng)及時(shí)撥打自行搶救,謝謝。端選型總體還是比較前后端分離的,不強(qiáng)制你使用某一種方案。是官方出品和推薦的,也是默認(rèn)的配套方案。事后來看,的坑不少。 apollo-client 是一個(gè)比較難用的 GraphQL 客戶端,本系列帶你集成 redux,趟平深坑,鉆入原理,讓你在 21 分鐘內(nèi)學(xué)完 apollo-client。 NOTE: 閱讀過程中如果產(chǎn)生任何不適,請(qǐng)...

    rockswang 評(píng)論0 收藏0
  • Graphql實(shí)踐——像axios一樣使用Graphql

    摘要:初始化項(xiàng)目使用初始化項(xiàng)目安裝項(xiàng)目結(jié)構(gòu)如下接口所有接口對(duì)封裝接下來對(duì)進(jìn)行封裝,加上中間件實(shí)現(xiàn)類似于攔截器的效果。 Graphql嘗鮮 在只學(xué)習(xí)graphql client端知識(shí)的過程中,我們常常需要一個(gè)graphql ide來提示graphql語法,以及實(shí)現(xiàn)graphql的server端來進(jìn)行練手。graphql社區(qū)提供了graphiql讓我們使用 graphiql (npm):一個(gè)交互...

    mumumu 評(píng)論0 收藏0
  • 翻譯 | React AJAX最佳實(shí)踐

    摘要:作者滬江前端開發(fā)工程師本文原創(chuàng)翻譯,有不當(dāng)?shù)牡胤綒g迎指出。管理數(shù)據(jù),而提供服務(wù)器上的數(shù)據(jù),因此應(yīng)用于處理網(wǎng)絡(luò)請(qǐng)求。結(jié)論使用建立的應(yīng)用都是模塊化的會(huì)成為其中一個(gè)模塊,庫是另一個(gè)模塊。原文原創(chuàng)新書移動(dòng)前端高效開發(fā)實(shí)戰(zhàn)已在亞馬遜京東當(dāng)當(dāng)開售。 作者:Oral (滬江Web前端開發(fā)工程師)本文原創(chuàng)翻譯,有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請(qǐng)指明出處。 當(dāng)你問起有關(guān)AJAX與React時(shí),老司機(jī)們首先就會(huì)...

    DirtyMind 評(píng)論0 收藏0
  • react如何和server交互

    摘要:在一個(gè)應(yīng)用中,如何通過和端進(jìn)行交互這個(gè)問題曾經(jīng)困擾了我一段時(shí)間,經(jīng)過學(xué)習(xí)實(shí)踐,有了一點(diǎn)心得體會(huì),寫出來和大家分享一下。組件和一樣,和進(jìn)行交互,將獲取的通過向下傳遞給組件。不足被設(shè)計(jì)用來和服務(wù)器一起運(yùn)行,并不能很好的和第三方服務(wù)交互。 在一個(gè)react應(yīng)用中,如何通過ajax和server端進(jìn)行交互這個(gè)問題曾經(jīng)困擾了我一段時(shí)間,經(jīng)過學(xué)習(xí)實(shí)踐,有了一點(diǎn)心得體會(huì),寫出來和大家分享一下。 總的...

    1treeS 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<