欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

GraphQL在react中的應(yīng)用示例詳解

 更新時(shí)間:2022年10月11日 09:46:08   作者:Flipped_Z  
這篇文章主要為大家介紹了GraphQL在react中的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

什么是 GraphQL

GraphQL由Facebook發(fā)起,其手機(jī)客戶端自2012年起,就全面采用了GraphQL查詢語(yǔ)言, 2015年, Facebook全面開(kāi)源了第一份GraphQL規(guī)范。 GraphQL 對(duì)你的 API 中的數(shù)據(jù)提供了一套易于理解的完整描述,是一種規(guī)范,使得客戶端能夠準(zhǔn)確地獲得它需要的數(shù)據(jù),而且沒(méi)有任何冗余.

GraphQL出現(xiàn)的意義

傳統(tǒng)API存在的主要問(wèn)題:

  • 接口數(shù)量眾多維護(hù)成本高:接口的數(shù)量通常由業(yè)務(wù)場(chǎng)景的數(shù)量決定,為了盡量減少接口數(shù)量,服務(wù)端工程師通常會(huì)對(duì)業(yè)務(wù)做抽象,首先構(gòu)建粒度較小的數(shù)據(jù)接口,再根據(jù)業(yè)務(wù)場(chǎng)景對(duì)數(shù)據(jù)接口進(jìn)行組合,對(duì)外暴露業(yè)務(wù)接口,即便這樣,服務(wù)端對(duì)前端暴露的接口數(shù)量還是非常多,因?yàn)闃I(yè)務(wù)總是多變的。
  • 接口擴(kuò)展成本高:出于帶寬的考慮移動(dòng)端我們要求接口返回盡量少的字段,PC 端通常要展現(xiàn)更多字段;考慮首屏性能,我們又要求對(duì)接口做合并;傳統(tǒng) API 應(yīng)對(duì)這些需求,前后端都面臨改造,成本較高。
  • 接口響應(yīng)的數(shù)據(jù)格式無(wú)法預(yù)知:由于接口文檔幾乎總是不能及時(shí)更新,前端工程師無(wú)法預(yù)知接口響應(yīng)的數(shù)據(jù)格式,影響前端開(kāi)發(fā)進(jìn)度。

GraphQL 如何解決問(wèn)題

請(qǐng)求參數(shù)在發(fā)送到服務(wù)端之前會(huì)先經(jīng)過(guò) GraphQL Client 轉(zhuǎn)換成客戶端 Schema,這段 Schema 其實(shí)是一段 query 開(kāi)頭的字符串,描述了客戶端的對(duì)數(shù)據(jù)的述求:調(diào)用哪個(gè)方法,傳遞什么樣的參數(shù),返回哪些字段。服務(wù)端拿到這段 Schema 之后,通過(guò)事先定義好的服務(wù)端 Schema 接收請(qǐng)求參數(shù)并執(zhí)行對(duì)應(yīng)的 resolve 函數(shù)提供數(shù)據(jù)服務(wù)。

GraphQL基本語(yǔ)法

參考 [GraphQL][1] 官網(wǎng)文檔

標(biāo)量類型

GraphQL 自帶一組默認(rèn)標(biāo)量類型: Int:有符號(hào) 32 位整數(shù)。 Float:有符號(hào)雙精度浮點(diǎn)值。 String:UTF‐8 字符序列。 Boolean:true 或者 false。 ID:ID 標(biāo)量類型表示一個(gè)唯一標(biāo)識(shí)符,通常用以重新獲取對(duì)象或者作為緩存中的鍵。ID 類型使用和 String 一樣的方式序列化。

對(duì)象類型

一個(gè) GraphQL schema 中的最基本的組件是對(duì)象類型,它就表示你可以從服務(wù)上獲取到什么類型的對(duì)象,以及這個(gè)對(duì)象有什么字段

type Character {
    name: String!
    list: [Episode!]!
}
myField: [String!]
myField: null 
myField: [] 
myField: ['a', 'b'] 
myField: ['a', null, 'b'] 
myField: [String]!
myField: null
myField: []
myField: ['a', 'b'] 
myField: ['a', null, 'b']

GraphQL 對(duì)象類型上的每一個(gè)字段都可能有零個(gè)或者多個(gè)參數(shù),

type Starship {
    id: ID!
    name: String!
    length(unit: LengthUnit = METER): Float
}

枚舉類型

enum Episode {
    NEWHOPE
    EMPIRE
    JEDI
}

這表示無(wú)論我們?cè)?schema 的哪處使用了 Episode,都可以肯定它返回的是 NEWHOPE、EMPIRE 和 JEDI 之一。

對(duì)象類型、標(biāo)量以及枚舉是 GraphQL 中你唯一可以定義的類型種類。但是當(dāng)你在 schema 的其他部分使用這些類型時(shí),或者在你的查詢變量聲明處使用時(shí),你可以給它們應(yīng)用額外的類型修飾符來(lái)影響這些值的驗(yàn)證。

type Character {
    name: String!
    list: [Episode]!
}

GraphQL 內(nèi)置指令

GraphQL 中內(nèi)置了兩款邏輯指令,指令跟在字段名后使用。

@include 當(dāng)條件成立時(shí),查詢此字段

query {
    search {
        actors @include(if: $queryActor) {
            name
        }
    }
}

@skip 當(dāng)條件成立時(shí),不查詢此字段

query {
    search {
        comments @skip(if: $noComments) {
            from
        }
    }
}

  • 操作類型:指定本請(qǐng)求體要對(duì)數(shù)據(jù)做什么操作,類似與 REST 中的 GET POST。GraphQL 中基本操作類型有 query 表示查詢,mutation 表示對(duì)數(shù)據(jù)進(jìn)行操作,例如增刪改操作,subscription 訂閱操作。
  • 操作名稱:操作名稱是個(gè)可選的參數(shù),操作名稱對(duì)整個(gè)請(qǐng)求并不產(chǎn)生影響,只是賦予請(qǐng)求體一個(gè)名字,可以作為調(diào)試的依據(jù)。
  • 變量定義:在 GraphQL 中,聲明一個(gè)變量使用符號(hào)開(kāi)頭,冒號(hào)后面緊跟著變量的傳入類型。如果要使用變量,直接引用即可,例如上面的movie就可以改寫(xiě)成movie(name:符號(hào)開(kāi)頭,冒號(hào)后面緊跟著變量的傳入類型。如果要使用變量,直接引用即可,例如上面的 movie 就可以改寫(xiě)成 movie(name: 符號(hào)開(kāi)頭,冒號(hào)后面緊跟著變量的傳入類型。如果要使用變量,直接引用即可,例如上面的movie就可以改寫(xiě)成movie(name:name)。
query Hero($episode: Int!, $withFriends: Boolean!) {
    hero(episode: $episode) {
        name
        friends @include(if: $withFriends) {
        name
        }
    }
}

什么是 Apollo

Meteor 團(tuán)隊(duì)有著很豐富的數(shù)據(jù)流控制經(jīng)驗(yàn),他們發(fā)現(xiàn)了 Relay 的不便之處,引領(lǐng)業(yè)界通過(guò)使用他們開(kāi)發(fā)的 Apollo 享受到更簡(jiǎn)潔的接口,Apollo 是基于GraphQL的全棧解決方案集合。包括了 apollo-client 和 apollo-server ;從后端到前端提供了對(duì)應(yīng)的 lib ,使開(kāi)發(fā)使用 GraphQL 更加的方便。

apollo-server

apollo-server是一個(gè)在nodejs上構(gòu)建grqphql服務(wù)端的web中間件。支持express,koa 等框架。 參考 [apollo-server][2] 官網(wǎng)文檔

處理流程

主要是通過(guò)官方graphql-js庫(kù)進(jìn)行處理

1.解析階段

為了識(shí)別客戶端 Schema, graphql-js 定義了一系列的特征標(biāo)識(shí)符:

export const TokenKind = Object.freeze({
    BANG: '!',
    DOLLAR: '$',
    PAREN_L: '(',
    PAREN_R: ')',
    SPREAD: '...',
    COLON: ':',
    EQUALS: '=',
    BRACKET_L: '[',
    BRACKET_R: ']',
    ...
});

并定義了 AST 語(yǔ)法樹(shù)規(guī)范,規(guī)定語(yǔ)法樹(shù)支持以下節(jié)點(diǎn):

export const Kind = Object.freeze({
    // Name
    NAME: 'Name',
    // Document
    DOCUMENT: 'Document',
    OPERATION_DEFINITION: 'OperationDefinition',
    VARIABLE_DEFINITION: 'VariableDefinition',
    VARIABLE: 'Variable',
    // Values
    INT: 'IntValue',
    FLOAT: 'FloatValue',
    STRING: 'StringValue',
    BOOLEAN: 'BooleanValue',
    ...
});

有了特征字符串與 AST 語(yǔ)法樹(shù)規(guī)范,GraphQL Server 對(duì)客戶端 Schema 進(jìn)行逐字符掃描,如果客戶端 Schema 不符合服務(wù)端定義的 AST 規(guī)范,解析過(guò)程會(huì)直接拋出語(yǔ)法異常。

2.校驗(yàn)階段

校驗(yàn)階段用于驗(yàn)證客戶端 Schema 是否按照服務(wù)端 Schema 定義好的方式獲取數(shù)據(jù),比如:獲取數(shù)據(jù)的方法名是否有誤,必填項(xiàng)是否有值等等,校驗(yàn)范圍一共有幾十種,不一一舉例。

{
    "errors":[
        {
            "message":"Cannot query field "getU" on type "Query". Did you mean "getUser"?",
            "locations":[
                {
                    "line":3,
                    "column":9
                }
            ]
        }
    ]
}

不僅返回結(jié)構(gòu)化的報(bào)錯(cuò)信息,還非常人性化的告訴你正確的調(diào)用方式是什么。校驗(yàn)階段通過(guò)之后會(huì)進(jìn)入執(zhí)行階段.

3.執(zhí)行階段

執(zhí)行階段依賴的輸入為:解析階段的產(chǎn)出物 document ,服務(wù)端 Schema;其中 document 準(zhǔn)確描述了客戶端對(duì)數(shù)據(jù)的述求:請(qǐng)求哪個(gè)方法,參數(shù)是什么,需要哪些字段;服務(wù)端 Schema 描述了提供數(shù)據(jù)的方式。執(zhí)行服務(wù)端 Schema 中的 resolve 函數(shù),得到執(zhí)行階段的輸出。每個(gè)類型的每個(gè)字段都由一個(gè) resolver 函數(shù)支持,該函數(shù)由 GraphQL 服務(wù)器開(kāi)發(fā)人員提供。

Schema

Schema可以說(shuō)是GraphQL最具核心的部分,其描述了整個(gè)接口向外暴露的形式;像Restful API,我們會(huì)定義一個(gè)查詢所有人的接口url定義為:/api/v1/user/getUsers,而查詢?nèi)司唧w信息的接口url為:/api/v1/user/getUserById,前端人員調(diào)用起來(lái)很直觀。但是graphql是完全不一樣的使用方式,其向前端暴露的url就一個(gè)像/api/graphql之類的,那這么多接口怎么區(qū)分呢?

一個(gè)graphql接口都有一個(gè)Schema定義,其定義三種操作方式:query(查詢),mutation(變更)和subscription(監(jiān)聽(tīng))。再往下延伸,一個(gè)查詢中包含多個(gè)field,也就是多種不同的查詢,比如query user查詢?nèi)?,query message查詢消息,query weather查詢天氣,通過(guò)這些就實(shí)現(xiàn)了Restful API使用多個(gè)url來(lái)達(dá)到不同操作的效果。

給server端帶來(lái)的便利性

由于 GraphQL 通過(guò)客戶端 Schema 而不是通過(guò) URL 描述數(shù)據(jù)述求,所以理論上服務(wù)端只需要對(duì)客戶端暴露一個(gè)地址即可, 解決了接口數(shù)量眾多維護(hù)成本高的問(wèn)題; 同時(shí),服務(wù)端提供的是全量字段,客戶端可按需獲取,面對(duì)接口擴(kuò)展的需求,服務(wù)端沒(méi)有開(kāi)發(fā)成本;

import express from 'express';
import { graphiqlExpress, graphqlExpress } from 'apollo-server-express';
const app = express();
app.use('/graphql', graphqlExpress({
    schema,
}));
app.use('/graphiql', graphiqlExpress({
    endpointURL: '/graphql'
}));

apollo-client

參考 [apollo-client][3] 官網(wǎng)文檔

創(chuàng)建client

import ApolloClient from "apollo-boost";
const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io"
});

在我們將Apollo Client連接到React之前,讓我們先嘗試發(fā)送查詢。記住首先導(dǎo)入gql用于將查詢字符串解析為查詢文檔。

import gql from "graphql-tag";
...
client.query({
    query: gql`
      {
        rates(currency: "USD") {
          currency
        }
      }
    `
  })
  .then(result => console.log(result));

將client注入到react

react-apollo提供ApolloProvider組件,ApolloProvider類似于redux的provider。它會(huì)把a(bǔ)pollo客戶端放入到React app的上下文里,以便在組件樹(shù)的任何地方都是可以獲取到apollo客戶端。

import React from "react";
import { render } from "react-dom";
import { ApolloProvider } from "react-apollo";
const App = () => (
  <ApolloProvider client={client}>
    <div>
      <h3>My first Apollo app ??</h3>
    </div>
  </ApolloProvider>
);
render(<App />, document.getElementById("root"));

數(shù)據(jù)請(qǐng)求

1.通過(guò)react-apollo提供的graphql函數(shù)獲取數(shù)據(jù),并connect到組建中,就可以在組件的this.props中看到多了個(gè)data對(duì)象,

import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
export const USERS_QUERY = gql`
  query UserQuery($pageNum: Int,$pageSize:Int){
    users(pageNum:$pageNum,pageSize:$pageSize ) {
      pageNum
      pageSize
      total
      data {
        userName
      }
    }
  }
`;
const withQuery = graphql(USERS_QUERY, {
  options: () => ({
    variables: {
      pageNum: 3,
      pageSize: 8,
    },
  }),
});
class List extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  render() {
    const { data: { loading, error, users } } = this.props;
    if (loading) {
      return <div className="loading">Loading...</div>;
    }
    if (error) return `Error! ${error.message}`;
    const { total } = users;
    };
    return (
        <div>
            <p className="total">總共<span>{total}</span>人</p>
        </div>
    );
  }
}
export default withQuery(List);

data中包含loading, error, users等字段

當(dāng)React安裝Query組件時(shí),Apollo Client會(huì)自動(dòng)觸發(fā)查詢。如果想延遲觸發(fā)查詢,直到用戶執(zhí)行操作(例如單擊按鈕),該怎么辦?對(duì)于這種情況,可以使用ApolloConsumer組件并直接調(diào)用client.query()。

import React, { Component } from 'react';
import { ApolloConsumer } from 'react-apollo';
class DelayedQuery extends Component {
  state = { dog: null };
  onDogFetched = dog => this.setState(() => ({ dog }));
  render() {
    return (
      <ApolloConsumer>
        {client => (
          <div>
            {this.state.dog && <img src={this.state.dog.displayImage} />}
            <button
              onClick={async () => {
                const { data } = await client.query({
                  query: GET_DOG_PHOTO,
                  variables: { breed: "bulldog" }
                });
                this.onDogFetched(data.dog);
              }}
            >
              Click me!
            </button>
          </div>
        )}
      </ApolloConsumer>
    );
  }
}

2.通過(guò)apollo提供的組件獲取

import gql from "graphql-tag";
import { Query } from "react-apollo";
const GET_DOG_PHOTO = gql`
  query Dog($breed: String!) {
    dog(breed: $breed) {
      id
      displayImage
    }
  }
`;
const DogPhoto = ({ breed }) => (
  <Query query={GET_DOG_PHOTO} variables={{ breed }} pollInterval={500}>
    {({ loading, error, data, startPolling, stopPolling }) => {
      if (loading) return null;
      if (error) return `Error!: ${error}`;
      return (
        <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
      );
    }}
  </Query>
);

通過(guò)設(shè)置pollInterval為500,每隔0.5秒看到一個(gè)新的小狗圖像。 當(dāng)我們從Query組件中獲取數(shù)據(jù)時(shí),看看Apollo Client幕后發(fā)生的事情。

1.當(dāng)Query組件安裝時(shí),Apollo Client會(huì)為我們的查詢創(chuàng)建一個(gè)observable。我們的組件通過(guò)Apollo Client緩存訂閱查詢結(jié)果。

2.首先,我們嘗試從Apollo緩存加載查詢結(jié)果。如果它不在那里,我們將請(qǐng)求發(fā)送到服務(wù)器。

3.數(shù)據(jù)恢復(fù)后,我們將其標(biāo)準(zhǔn)化并將其存儲(chǔ)在Apollo緩存中。由于Query組件訂閱了結(jié)果,因此它會(huì)自動(dòng)更新數(shù)據(jù)。

數(shù)據(jù)緩存

創(chuàng)建本地緩存

import { ApolloClient } from 'apollo-client'
import { withClientState } from 'apollo-link-state'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { resolvers, typeDefs, defaults } from '../client/index'
const cache = new InMemoryCache()
const client = new ApolloClient({
  cache, // 本地?cái)?shù)據(jù)存儲(chǔ)
  link: withClientState({ resolvers, defaults, cache, typeDefs }).concat(
    new HttpLink({
      uri: 'http://localhost:4001/graphql',
      opts: {
        credentials: 'cross-origin',
      },
    })
  ),
})

要直接與緩存交互,可以使用Apollo Client方法readQuery,readFragment,writeQuery和writeFragment。

總結(jié)

如果使用 GraphQL,那么后端將不再產(chǎn)出 API,而是將 Controller 層維護(hù)為 Resolver,和前端約定一套 Schema,這個(gè) Schema 將用來(lái)生成接口文檔,前端直接通過(guò) Schema 或生成的接口文檔來(lái)進(jìn)行自己期望的請(qǐng)求。

GraphQL 的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

所見(jiàn)即所得:所寫(xiě)請(qǐng)求體即為最終數(shù)據(jù)結(jié)構(gòu) 減少網(wǎng)絡(luò)請(qǐng)求:復(fù)雜數(shù)據(jù)的獲取也可以一次請(qǐng)求完成 Schema 即文檔:定義的 Schema 也規(guī)定了請(qǐng)求的規(guī)則 類型檢查:嚴(yán)格的類型檢查能夠消除一定的認(rèn)為失誤

缺點(diǎn)

增加了服務(wù)端實(shí)現(xiàn)的復(fù)雜度:一些業(yè)務(wù)可能無(wú)法遷移使用 GraphQL,雖然可以使用中間件的方式將原業(yè)務(wù)的請(qǐng)求進(jìn)行代理,這無(wú)疑也將增加復(fù)雜度和資源的消耗

以上就是GraphQL在react中的應(yīng)用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于GraphQL react應(yīng)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • VSCode配置react開(kāi)發(fā)環(huán)境的步驟

    VSCode配置react開(kāi)發(fā)環(huán)境的步驟

    本篇文章主要介紹了VSCode配置react開(kāi)發(fā)環(huán)境的步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • react實(shí)現(xiàn)簡(jiǎn)單的拖拽功能

    react實(shí)現(xiàn)簡(jiǎn)單的拖拽功能

    這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)簡(jiǎn)單的拖拽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 使用 Rails API 構(gòu)建一個(gè) React 應(yīng)用程序的詳細(xì)步驟

    使用 Rails API 構(gòu)建一個(gè) React 應(yīng)用程序的詳細(xì)步驟

    這篇文章主要介紹了使用 Rails API 構(gòu)建一個(gè) React 應(yīng)用程序的詳細(xì)步驟,主要包括后端:Rails API部分,前端:React部分及React組件的相關(guān)操作,具有內(nèi)容詳情跟隨小編一起看看吧
    2021-08-08
  • react-router-dom 嵌套路由的實(shí)現(xiàn)

    react-router-dom 嵌套路由的實(shí)現(xiàn)

    這篇文章主要介紹了react-router-dom 嵌套路由的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • React中如何處理承諾demo

    React中如何處理承諾demo

    這篇文章主要為大家介紹了React中如何處理承諾demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React報(bào)錯(cuò)Type '() => JSX.Element[]' is not assignable to type FunctionComponent

    React報(bào)錯(cuò)Type '() => JSX.Element[]&apos

    這篇文章主要為大家介紹了React報(bào)錯(cuò)Type '() => JSX.Element[]' is not assignable to type FunctionComponent解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React+CSS 實(shí)現(xiàn)繪制橫向柱狀圖

    React+CSS 實(shí)現(xiàn)繪制橫向柱狀圖

    這篇文章主要介紹了React+CSS 實(shí)現(xiàn)繪制橫向柱狀圖,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • React事件節(jié)流效果失效的原因及解決

    React事件節(jié)流效果失效的原因及解決

    這篇文章主要介紹了React事件節(jié)流效果失效的原因及解決,幫助大家更好的理解和學(xué)習(xí)使用React框架,感興趣的朋友可以了解下
    2021-04-04
  • 對(duì)react中間件的理解

    對(duì)react中間件的理解

    這篇文章主要介紹了對(duì)react中間件的理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn)

    React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn)

    這篇文章主要介紹了React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02

最新評(píng)論