GrowingIO 前端團隊對於 GraphQL 的實踐總結

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"社區裏已有很多有關 GraphQL 入門和原理的文章,GraphQL 和 Apollo Client 的官方網站也有較爲詳細的介紹。相對於 RESTful API(簡稱 REST),社區也從各個方面分析了 GraphQL 的優劣利弊。本文主要是從前端角度出發,在如今前後端開發分離盛行的前提下,分享一些我們如何利用社區中熱門的工具提高開發效率和工程質量的經驗,希望對已經決心入坑 GraphQL 的朋友們有一些幫助。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL Playground","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一次接觸 GraphQL 的時候一定會從官方文檔裏瞭解到 GraphQL Server 所提供的 Playground。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Playground 是一個完整的瀏覽器端 GraphQL IDE, 包括 Documentation, Schema 和一個可以編寫和測試 GraphQL 請求的編輯器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/6235d8cc4906a9055b1879642ef03fbd.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於使用 GraphQL 的開發團隊, Playground 基本可以代替如 Swagger 之類的的 API 文檔和如 Postman 之類的的請求模擬工具。是一個較完備的開發手冊,並且優點是零成本使用, 並且有團隊在維護和迭代。GraphQL 社區絕大多數對於GraphQL Server的實現都會內置 Playground。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Apollo Client Devtool","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/89/8936576bd8962817e521906202a1ed7b.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Apollo Client Devtool 是 Apollo Client 提供的一款 Chrome 插件,可以在瀏覽器的 Console 中捕捉頁面發送的 Query 和 Mutation,並且提供了編輯,修改和重試這些請求,還有最重要的查看 GraphQL Client 緩存的功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲所有的 GraphQL 都是發送給統一的路徑,Query Body 也是以字符串的形式發送,使得請求的可讀性很差, Apollo Client Devtool 相較於通過傳統 Network 的方式做了優化。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上兩個工具可以說是入門必備,類似我們前後端對接 Restful Api 時我們會用到的工具。GraphQL 依賴 Schema 提供了更多的提效工具。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GrahpQL VS Code Plugin","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/67/67c1fdb7b2b7fd66bbfabb89276a1b72.gif","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Visual Studio Code(簡稱 VS Code) Plugin GraphQL 提供了相當齊全的功能,包括語法檢查,語法高亮,引用跳轉,Schema 懸浮提示,Autocomplete 等。是一個必裝的插件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很重要的一個功能是,當我們在定義 Schema 的時候對每一個參數所定義的描述(有的實現是通過代碼註釋的方式插入 Schema )是可以通過這個插件的提示在 IDE 裏展示的,也就是原本前後兩端都需要的註釋內容,包括對接口的描述,對接口參數的描述等, 現在可以只用在 Schema 裏定義一遍就可以了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d0/d08c1208bd7c10d6974fa36e2d3e9f7c.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/afa270a13fdab9a397be530ce1e5baf8.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該 Plugin 還提供了一個可以在 IDE 內執行 Query 和 Mutation 的功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/85/85d90c96caf258feb47d169606a2e62c.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是個人認爲實用性有限,對於不含參數的 Query 查詢可以直接執行,對於需要傳入參數的 Query 和 Mutation 需要在 VS Code 的彈窗中輸入參數,如果是較爲簡單的字符串或者數字參數比較方便,但是如果是較爲複雜的結構體就需要輸入 Stringfy 過後的 JSON 格式,並且很多情況下鏈式請求的參數依賴上一個請求,發送請求所需的參數就更不容易手動獲取了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6c/6cee846ebdb8d0f30a36da8739558e7c.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上工具可以幫助我們更方便的查詢和編寫 Query, 但只有這些功能還是遠遠不夠的,我們需要藉助工具將 Schema 融入前端工程化的過程,提效的同時提高代碼的健壯性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL ESLint","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b1/b10d3ae795056b4b9188c84f7e4bd54f.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GraphQL ESLint 最基礎的前端代碼檢查就是 ESLint,我們有了統一的 Schema 之後, 就可以通過 Schema 生成的對應的規則來對 Query 進行類似Playground 中 Syntax 檢查。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL Code Generator","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GraphQL Code-Generator  最能提高前端代碼健壯性的方法無疑是 TypeScript , 但是根據 Schema 手寫項目全套的類型文件會有很大的工作量, GraphQL Code Generator 是一個根據 Schema 生成各類語言類型文件的 cli 工具, 工具本身是通過 Preset 和 Plugin 的方式來實現針對不同語言和功能的需求,官方本身也提供了很豐富的 Preset 和 Plugin 可以滿足大部分的開發需求,自定義 Plugin 也有較爲詳細的文檔和示例。這裏我們只討論 Typescript 和相關的 Plugin。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a2/a2c4f5217e91e12d31610f8e9ab60bdc.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最核心的一點是通過 Code Generator 生成的 Type 文件是包含 Schema 描述的。所以我們在使用 GraphQL 返回的數據時,Typescript 是會提示數據類型的描述的。如下圖,關於 Event 對象中 Description 字段的描述 “Maximum 50 characters” 是後端在定義 Schema 時寫的註釋,通過這個生成的類型文件,成爲了前端 Typescript 類型文件中的註釋,可以被 IDE 完美引用。等於說後端同學在寫代碼註釋時,也在幫前端同學寫代碼註釋。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/17/17abf4c7b8c16e5c61f382d49a151f4e.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面就是我們該如何使用這些類型文件的問題,以在 VS Code Typescript 插件的幫助下我們在使用 UseQuery Hook 時,定義好 UseQuery 的僞類 就可以在使用返回數據時擁有完整的數據類型提醒,Autocomplete 和定義 Schema 時對字段的具體描述。這種方式在開發體驗和效率上相比傳統在項目和文檔間來回切換提高了很多,也同時增強了代碼的健壯性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是這種方式也存在一個問題,就是細心的朋友可能發現我們在定義Query的時候只請求了 Events 對象的 Name 屬性,但是自動提示卻提示了 Events 包含的所有可能字段,並不能完全滿足我們的要求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看一下 UseQuery 方法的類型定義:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"useQuery: TData = any, TVariables = OperationVariables: (query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions): QueryResult;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接受兩個僞類  TData 和 TVaraibles,我們需要在使用 Hooks 的時候傳入這兩個參數,如果要根據 Query 來自定義返回值,我們需要在第一個參數傳入被Pick 或 Exclude 包裝過後的類型,如果 Query 或 Mutation 有傳入參數也需要從根路徑的 Type。TS 去引用 Input 的類型,非常麻煩。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":" {events: Pick[]}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"query GetEvents{  events { id name  }}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Code-Generator 針對這個問題也有相應的方案,通過 Typescript-Operations 這個 Plugin 生成的文件生成的類型文件會自動排除缺省的字段","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"export type GetEventsQueryVariables = SchemaTypes.Exact;\n// With Pick export type GetEventsQuery = { events?: SchemaTypes.Maybe>>> };\n// Without Pickexport type GetEventsQuery = { events?: SchemaTypes.Maybe }>>> };","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於使用 React + Apollo Client 的項目,typescript-react-apollo plugin 不僅可以生成類型,還可以可以生成和 Query 相對應的請求的 hook 包括封裝好的UseQuery, UseLazyQuery 等,可以在保證代碼類型完整的前提下極大的提高開發效率。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3f/3fe4e3e7ce95fd3078d50695d0fcd87b.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Code Generator 的插件很多,配置項也很多,自定義 Plugin 也比較容易,可以根據根據項目需要,配置出適合項目適合團隊的工程化方案。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL Introspection","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上提到的所有工具都是依靠 GraphQL 標準中提供的 Introspection 接口實現的,該接口的能力其實也很簡單明瞭,就是規定了對於 GraphQL 請求的根部永遠是 __schema 節點,可以通過這個節點來查詢整個 Schema 的內容。社區裏的絕大部分工具也都是通過這個接口的能力開發。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Introspection, 感興趣的朋友也可以參考 GraphQL 的官方文檔去開發適合自己的工具。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL Config","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後要提的是 GraphQL Config, 一份配置文件支持所有和 GraphQL 相關的工具。該配置文件支持多種文件類型,例如 Json、Yaml、 Yml、 JS、TS 等等,社區裏大部分工具都已支持 GraphQL Config 配置文件,GraphQL 官方也建議開發者使用 GraphQL Config 來作爲配置文件。如果開發自己的工具也可以使用 GraphQL Config 作爲配置文件。我們在使用和分享 GraphQL 生態中的這些工具時,只依賴一份配置文件是非常方便的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"schema: './schema/*.GraphQL'extensions:  codegen:    generates: ./src/types.ts:        plugins: - typescript          - typescript-resolvers","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GraphQL 給予了前端工程在請求數據方面更好的靈活性,更爲健全的類型定義,同時也爲使用 Typescript 帶來了更多的工作量。但是在社區的支持下,合理的運用工具可以在保證前端項目類型安全的前提下,極大的提高工程效率和工程質量,也從側面解決了一些老生常談的文檔問題和註釋問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考目錄","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Apollo Grpahql (https://www.apollographql.com/)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Graphql-playground (https://github.com/GraphQL/GraphQL-playground)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Apollo Client Devtools (https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"GraphQL VS Code Plugin (GraphQL - Visual Studio Marketplace)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Graphql-eslint (dotansimha/graphql-eslint)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Graphql-code-generator (https://www.graphql-code-generator.com/)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Graphql Orgnization (https://graphql.org/)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Graphql Config (https://graphql-config.com/)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4f/4f723eac3769618f47affa9ddad2d0c6.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"作者:周兆庭 / 前端開發工程師","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"招聘信息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GrowingIO技術團隊是一個活力四射、對技術充滿激情的團隊,多個崗位持續招聘中!誠招前端工程師/大數據工程師/Java工程師等
,歡迎有興趣的同學投遞簡歷至:[email protected](郵件標題請註明具體崗位名稱)
,更多職位及信息可進入招聘官網查看
。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"關於 GrowingIO","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GrowingIO 是國內領先的一站式數據增長引擎整體方案服務商,創立於2015年,以數據智能分析能力爲核心,通過構建客戶數據平臺,打造增長營銷閉環,幫助企業提升數據驅動能力,賦能商業決策、實現業務增長。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章