從GraphQL到前端數據管理的革命 - GraphQL乾貨筆記

你知道嗎?FaceBook、GitHub,Pinterest,Twitter,Sky,紐約時報,Shopify,Yelp這些大公司已經在使用GraphQL規範的接口規範了。再不學習就落後了。javascript

代碼GitHub連接

參考文章

你將Get到的技能

  • GraphQL概念
  • 實戰GraphQL - Query/Mutation/Subscription
  • Rest vs GraphQL 對比分析
  • 如何搭建GraphQL後端服務
  • 如何利用React hooks 、Provider封裝GraphQL客戶端
  • 如何經過GraphQL完成對數據變化的訂閱

SPA框架崛起帶來的問題

在前端的開發中是否會遇到這樣的困擾?php

1. 面對複雜場景的API粒度問題

  • 減小請求次數就須要合併請求
  • 多端應用Web、App、小程序視圖不一樣因此須要的接口也不一樣
  • API接口粒度難於肯定
    • 粗粒度:移動端沒必要要的流量耗損
    • 細粒度:形成函數爆炸 (Function Explosion)

1. API版本劃分問題

  • 須要頻繁應對API版本的演進

2. 實現雙向通信時接口風格不統一

若是須要實現支付狀態、或者多人協做 、實時同步股票信息向客戶端推送數據時,每每須要使用WebSocket通信或者其餘通信方式。這時你會發現若是向服務器請求使用Restful風格沒法保證接口風格統一。css

3. 組件須要各自管理狀態

  • 組件須要將異步請求狀態分發
  • 父子組件通信使結構複雜
  • 訂閱的數據響應會使得數據流變得雜亂無章可讀性變差

GraphQL概覽

英文:graphql.org/html

中文: graphql.cn/前端

Github GraphQL Explorer developer.github.com/v4/explorer…java

概念

  • GraphQL 是由 Facebook 創造的用於 API 的查詢語言。react

  • 先後端數據查詢方式的規範。ios

GraphQL 既是一種用於 API 的查詢語言也是一個知足你數據查詢的運行時。 GraphQL 對你的 API 中的數據提供了一套易於理解的完整描述,使得客戶端可以準確地得到它須要的數據,並且沒有任何冗餘,也讓 API 更容易地隨着時間推移而演進,還能用於構建強大的開發者工具。git

優點

  • 精肯定義所需數據的能力(GraphQL擁有強類型)
# 查詢
query {
  book(id: "1") {
    id,
    author,
  }
}
# 結果
{
  "data": {
    "book": {
      "id": "1",
      "author": "Author1"
    }
  }
}
複製代碼
  • GraphQL容許您經過一次調用替換多個REST請求以獲取指定的數據
  • 描述全部可能類型的系統
# 查詢
query {
  book(id: "1") {
    id,
    author,
  },
  book2 : book(id:"3"){
    id
  }
}
# 結果
{
  "data": {
    "book": {
      "id": "1",
      "author": "Author1"
    },
    "book2": {
      "id": "3"
    }
  }
}
複製代碼
  • API 演進無需劃分版本

給你的 GraphQL API 添加字段和類型而無需影響現有查詢。老舊的字段能夠廢棄,從工具中隱藏。經過使用單一演進版本,GraphQL API 使得應用始終可以使用新的特性,並鼓勵使用更加簡潔、更好維護的服務端代碼。github

GraphQL VS Restful

擴展閱讀 REST, GraphQL, Webhooks, & gRPC 如何選型

GraphQL Restful
一次請求多資源 ✔️
API字段定製化 ✔️
精肯定義返回類型 ✔️
無需劃分版本 ✔️
類型驗證機制 ✔️
支持雙向通信 ✔️

基礎操做

語法說明 graphql.cn/learn/queri…

[進階閱讀複雜語法 Fragments Directives Function] juejin.cn/post/684490…

Query

普通查詢
query {
	books {
    title,
    author
  }
}

### Result
{
  "data": {
    "books": [
      {
        "title": "abc",
        "author": "xxxx"
      }
    ]
  }
}
複製代碼
帶有參數和變量和別名的查詢
# 查詢
 query($id:String) {
  book(id: $id) {
    id,
    author,
  },
  book2 : book(id:"3"){
    id
  }
}

# 變量
{
  "id":"1"
}

# 結果
{
  "data": {
    "book": {
      "id": "1",
      "author": "Author1"
    },
    "book2": {
      "id": "3"
    }
  }
}
複製代碼

Mutation

對於數據改變這種非冪等性操做使用Mutation來進項描述。

REST 中,任何請求均可能最後致使一些服務端反作用,可是約定上建議不要使用 GET 請求來修改數據。GraphQL 也是相似 —— 技術上而言,任何查詢均可以被實現爲致使數據寫入。然而,建一個約定來規範任何致使寫入的操做都應該顯式經過變動(mutation)來發送。

# 查詢
mutation {
  createBook(title:"TTTT",author: "AAA") {
    id
  }
}
複製代碼
Subscription

若是數據發生變化但願後臺主動通知前端,你可使用Subscription後臺消息訂閱功能。

subscription  {
	subsBooks
}

複製代碼

ApolloServer後端

ApolloServer是一個開源的GraphQL框架。ApolloServer能夠單獨的做爲服務器,同時ApolloServer也能夠做爲Express,Koa等Node框架的插件。

HelloWorld

const { ApolloServer, gql } = require('apollo-server');

// Schema定義
const typeDefs = gql`
  type Query {
    hello: String,
  }
`;

// 解釋器實現
const resolvers = {
    Query: {
        hello: () => 'Hello world!',
    }
};

// 建立服務器實例
const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
    console.log(`🚀  Server ready at ${url}`);
});
複製代碼

Schema定義小結

數據類型

GraphQL也有幾個基礎類型,在GraphQL中他們統稱叫標量類型(Scalar Type)

  • Int(整型)
  • Float(浮點型)
  • String(字符串)
  • Boolean(布爾型)
  • ID(惟一標識符類型)
  • 自定義類型 例如:Date類型,只需實現相關的序列化,反序列化和驗證的功能便可
對象類型

咱們能夠根據須要將數據類型組合爲對象這些統稱爲對象類型。

type Book {
    id:String
    title: String
    author: String
  }
複製代碼
其餘類型

爲了達到更好的代碼複用GraphQl還提供更爲複雜的接口類型這裏面就不在一一贅述。

  • Enumeration types(枚舉類型)
  • Union types(聯合類型)
  • Interface(接口)

Query

// index.js
// 添加Schema
const typeDefs = gql` type Query { books: [Book], book(id : String) : Book } type Book { id:String title: String author: String } `;


// 建立數據
const books = (
    () => Array(5).fill().map((v, i) => ({
        id: '' + i,
        title: 'Title' + i,
        author: 'Author' + i
    }))
)()

// 添加resolve
const resolvers = {
    Query: {
        
        books: () => books,
        book: (parent, { id }) => {
            return books.find(v => v.id === id)
        }
    },
}
複製代碼

Mutation

const typeDefs = gql` type Mutation { createBook(title: String, author: String): Book!, clearBook : Boolean } `
resolvers.Mutation = {
    createBook: (parent, args) => {
        const book = { ...args, id: books.length + 1 + '' }
        books.push(book)
        return book
    },
    clearBook: () => {
        books.length = 0
        return true
    }
 }
複製代碼

Subscription

const { ApolloServer, gql, PubSub, withFilter } = require('apollo-server');
const typeDefs = gql` type Subscription { subsBooks : Boolean, } `;
const pubsub = new PubSub()
resolvers.Mutation = {
    createBook: (parent, args) => {
        const book = { ...args, id: books.length + 1 + '' }
        books.push(book)
        // 發佈訂閱消息
        pubsub.publish('UPDATE_BOOK', {
            subsBooks: true
        })
        return book
    },
    clearBook: () => {
        books.length = 0
        // 發佈訂閱消息
        pubsub.publish('UPDATE_BOOK', {
            subsBooks: true
        })
        return true
    }
}
resolvers.Subscription = {
    subsBooks: {
        // 過濾不須要訂閱的消息
        subscribe: withFilter(
            (parent, variables) => pubsub.asyncIterator('UPDATE_BOOK'),
            (payload, variables) => true
        )
    },
}

複製代碼

GraphQL客戶端通信(Axios)

Query

<script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>
<script> axios .post("http://localhost:4000/graphql", { query: `query { books { id title author } }` }) .then(res => { console.log("res: ", res); document.writeln(JSON.stringify(res.data)) }); </script>
複製代碼

Mutataion

<script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>
<script> axios .post("http://localhost:4000/graphql", { query: `mutation($title:String,$author:String) { createBook(title:$title,author:$author){ id } }`, variables: { title: "TTTTT", author: "AAAAA" } }) .then(res => { console.log("res: ", res); document.writeln(JSON.stringify(res.data)) }); </script>
複製代碼

GraphQL響應式數據應用

[響應式 GraphQL 結構

](github.com/xitu/gold-m…)

ApolloClient (React)實現全局狀態管理

參考文章 www.zcfy.cc/article/the…

前端數據管理Redux(指令時) vs Apollo(聲明式)

在使用Apollo時咱們能夠嘗試一種徹底不一樣的前端數據管理方式,即聲明式數據管理。在傳統的項目中咱們一般會將數據存放在Redux這樣的統一狀態管理模塊中。利用ApolloClient經過GraphQL數據聲明的方式管理數據。每一個模塊均可以根據本身的需求定製好本身想要的數據。

數據鏈接Provider

const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
});

ReactDOM.render(
  <React.StrictMode> <ApolloProvider client={client}> <App /> </ApolloProvider> </React.StrictMode >, document.getElementById('root') ); 複製代碼

須要使用Subscription的時候須要符合鏈接

// Subscription
// Create an http link:
const httpLink = new HttpLink({
  uri: 'http://localhost:4000/graphql'
});

// Create a WebSocket link:
const wsLink = new WebSocketLink({
  uri: `ws://localhost:4000/graphql`,
  options: {
    reconnect: true
  }
});

// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
  // split based on operation type
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);


// Subscription
const cache = new InMemoryCache();
const client = new ApolloClient({
  link,
  cache
});

複製代碼

Query

import React, { useEffect } from 'react';
import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';

const QUERY = gql` query { books { id, author, title } } `;


function Query() {
    const { loading, error, data, refetch } = useQuery(QUERY)

    useEffect(() => {
        refetch()
    })

    if (loading) return <p>Loading...</p>
    if (error) return <p>Error :(</p>
    console.log('book', data)
    const list = data.books.map(v => (
    <div>{v.author}: {v.title}</div>
    ))

    return list
}

export default Query;

複製代碼

Mutation

import React from 'react';
import { useMutation } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';

const CREATE_BOOK = gql`
    mutation CreateBook($title:String!,$author:String!){
        createBook(title:$title,author:$author){
            id,
            title,
            author
        }
    }
`;

const CLEAR_BOOK = gql`
    mutation {
        clearBook
    }
`;

function Mutation() {
    const [create, { data }] = useMutation(CREATE_BOOK);

    const [clear] = useMutation(CLEAR_BOOK)

    return (
        <div>
            <form
                onSubmit={e => {
                    e.preventDefault();
                    create({
                        variables: {
                            "title": 'Title' + (Math.random() * 100).toFixed(),
                            "author": 'Author'+ (Math.random() * 100).toFixed()
                        }
                    });
                    console.log('mutation:',data)
                }}
            >
                
                <button type="submit">Create</button>
            </form>
            <button onClick={ clear }>Clear</button>
        </div>
    );
}

export default Mutation;

複製代碼

Subscription

import React from 'react';
import { useSubscription } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
import Query from './Query'

const subs = gql` subscription { subsBooks } `;

function Subscription() {
    useSubscription(subs)
    return <Query/>
}

export default Subscription;

複製代碼

附錄

Apollo資源參考這篇文章 juejin.cn/post/684490…

  • 服務端

  • 客戶端

    • Relay Facebook 的 GraphQL 工具。
  • Prisma 彌合了數據庫和GraphQL resolvers之間的鴻溝,讓實現生產級別的GraphQL服務器變得更加容易。 除了強大的查詢引擎和API,Prisma在開發體驗方面尤其突出。www.prisma.io/

  • typeorm 直接複用typegraphql中建立的model

相關文章
相關標籤/搜索