GraphQL - buforowanie

Buforowanie to proces przechowywania danych w tymczasowym obszarze pamięci o nazwie cache. Po powrocie do ostatnio odwiedzonej strony przeglądarka może pobrać te pliki z pamięci podręcznej, a nie z oryginalnego serwera. Oszczędza to czas i sieć przed obciążeniem dodatkowym ruchem.

Aplikacje klienckie współpracujące z GraphQL są odpowiedzialne za buforowanie danych na ich końcu. Jednym z możliwych wzorców jest zarezerwowanie pola, takiego jak id, jako globalnego unikalnego identyfikatora.

InMemory Cache

InMemoryCache to znormalizowany magazyn danych powszechnie używany w aplikacjach klienckich GraphQL bez użycia innej biblioteki, takiej jak Redux.

Przykładowy kod do używania InMemoryCache z ApolloClient jest podany poniżej -

import {ApolloClient, HttpLink, InMemoryCache} from 'apollo-boost'
const cache = new InMemoryCache();

const client = new ApolloClient({
   link: new HttpLink(),
   cache
});

Konstruktor InMemoryCache przyjmuje opcjonalny obiekt konfiguracyjny z właściwościami umożliwiającymi dostosowanie pamięci podręcznej.

Sr.No. Parametr i opis
1

addTypename

Wartość logiczna określająca, czy dodać __typename do dokumentu (domyślnie: true)

2

dataIdFromObject

Funkcja, która przyjmuje obiekt danych i zwraca unikalny identyfikator, który ma być używany podczas normalizowania danych w sklepie

3

fragmentMatcher

Domyślnie InMemoryCache używa heurystycznego dopasowania fragmentów

4

cacheRedirects

Mapa funkcji służących do przekierowywania zapytania do innego wpisu w pamięci podręcznej przed wysłaniem żądania.

Ilustracja

Stworzymy aplikację jednostronicową w ReactJS z dwoma zakładkami - jedną dla zakładki domowej i drugą dla studentów. Karta studentów załaduje dane z API serwera GraphQL. Aplikacja zapyta o dane uczniów, gdy użytkownik przejdzie z karty głównej do karty uczniów. Wynikowe dane zostaną zapisane w pamięci podręcznej aplikacji.

Zapytamy również o czas serwera za pomocą getTimepole do sprawdzenia, czy strona jest buforowana. Jeśli dane zostaną zwrócone z pamięci podręcznej, strona wyświetli czas pierwszego żądania wysłanego do serwera. Jeśli dane są wynikiem nowego żądania skierowanego do serwera, zawsze będzie pokazywał najnowszy czas z serwera.

Konfigurowanie serwera

Poniżej przedstawiono kroki konfiguracji serwera -

Krok 1 - Pobierz i zainstaluj wymagane zależności dla projektu

Utwórz folder cache-server-app. Zmień katalog na cache-server-app z terminala. Wykonaj kroki od 3 do 5 opisane w rozdziale Konfiguracja środowiska.

Krok 2 - Utwórz schemat

Dodaj schema.graphql plik w folderze projektu cache-server-app i dodaj następujący kod -

type Query {
   students:[Student]
   getTime:String
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   fullName:String
}

Krok 3 - Dodaj resolwery

Utwórz plik resolvers.js w folderze projektu i dodaj następujący kod -

const db = require('./db')

const Query = {
      students:() => db.students.list(),
      getTime:() => {
      const today = new Date();
      var h = today.getHours();
      var m = today.getMinutes();
      var s = today.getSeconds();
      return `${h}:${m}:${s}`;
   }
}
module.exports = {Query}

Krok 4 - Uruchom aplikację

Utwórz plik server.js. Zapoznaj się z krokiem 8 w rozdziale Konfiguracja środowiska. Wykonaj polecenie npm start w terminalu. Serwer będzie działał na porcie 9000. Tutaj użyjemy GraphiQL jako klienta do przetestowania aplikacji.

Otwórz przeglądarkę i wprowadź adres URL http://localhost:9000/graphiql. Wpisz następujące zapytanie w edytorze -

{
   getTime
   students {
      id
      firstName
   }
}

Przykładowa odpowiedź zawiera nazwiska uczniów i czas serwera.

{
   "data": {
      "getTime": "22:18:42",
      "students": [
         {
            "id": "S1001",
            "firstName": "Mohtashim"
         },
         {
            "id": "S1002",
            "firstName": "Kannan"
         },
         {
            "id": "S1003",
            "firstName": "Kiran"
         }
      ]
   }
}

Konfigurowanie klienta ReactJS

Otwórz nowy terminal dla klienta. Terminal serwera powinien być uruchomiony przed wykonaniem aplikacji klienckiej. Aplikacja React będzie działać na porcie numer 3000, a aplikacja serwerowa na porcie numer 9000.

Krok 1 - Utwórz aplikację React

W terminalu klienta wpisz następujące polecenie -

npx create-react-app hello-world-client

Spowoduje to zainstalowanie wszystkiego, co jest potrzebne w typowej aplikacji Reag. Pliknpx utility i create-react-appnarzędzia tworzą projekt o nazwie hello-world-client. Po zakończeniu instalacji otwórz projekt w VSCode.

Zainstaluj moduły routera do reakcji za pomocą następującego polecenia - npm install react-router-dom.

Krok 2 - Uruchom hello-world-client

Zmień bieżącą ścieżkę folderu w terminalu na hello-world-client. Wpisz npm start, aby uruchomić projekt. Spowoduje to uruchomienie serwera programistycznego na porcie 3000 i automatycznie otworzy przeglądarkę i załaduje stronę indeksu.

Jest to pokazane na zrzucie ekranu podanym poniżej -

Krok 3 - Zainstaluj biblioteki klienckie Apollo

Aby zainstalować klienta Apollo, otwórz nowy terminal i znajdź bieżącą ścieżkę folderu projektu. Wpisz następujące polecenie -

npm install apollo-boost graphql

Spowoduje to pobranie bibliotek graphql po stronie klienta, a także pakietu Apollo Boost. Możemy to zweryfikować, wpisując zależności apollo-boost npm view. Będzie to miało wiele zależności, jak pokazano poniżej -

{ 
   'apollo-cache': '^1.1.15',
   'apollo-cache-inmemory': '^1.2.8',
   'apollo-client': '^2.4.0',
   'apollo-link': '^1.0.6',
   'apollo-link-error': '^1.0.3',
   'apollo-link-http': '^1.3.1',
   'apollo-link-state': '^0.4.0',
   'graphql-tag': '^2.4.2' 
}

Widzimy wyraźnie, że biblioteka klienta apollo jest zainstalowana.

Krok 4 - Zmodyfikuj składnik aplikacji w pliku index.js

Aby uzyskać prostą aplikację Reaguj, wystarczy zachować rozszerzenie index.js w src folder i index.htmlw folderze publicznym; wszystkie inne pliki, które są generowane automatycznie, można usunąć.

Strukturę katalogów podano poniżej -

hello-world-client /
   -->node_modules
   -->public
      index.html
   -->src
      index.js
      students.js
   -->package.json

Dodaj dodatkowy plik Students.js, który będzie zawierał składnik Students. Szczegóły ucznia są pobierane za pośrednictwem komponentu ucznia. W składniku aplikacji używamy HashRoutera.

Poniżej znajduje się index.js w aplikacji reagującej -

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {HashRouter, Route, Link} from 'react-router-dom'

//components
import Students from './students'
class App extends Component {
   render() {
      return(
         <div><h1>Home !!</h1>
         <h2>Welcome to React Application !! </h2>
         </div>
      )
   }
}

function getTime() {
   var d = new Date();
   return d.getHours()+":"+d.getMinutes()+":"+d.getSeconds()
}

const routes = <HashRouter>
   <div>
      <h4>Time from react app:{getTime()}</h4>
      <header>
         <h1>  <Link to="/">Home</Link> 
         <Link to = "/students">Students</Link>  </h1>
      </header>
      <Route exact path = "/students" component = {Students}></Route>
      <Route exact path = "/" component = {App}></Route>
   </div>
</HashRouter>

ReactDOM.render(routes, document.querySelector("#root"))

Krok 5 - Edytuj uczniów składowych w Students.js

W komponencie dla studentów zastosujemy następujące dwa podejścia do ładowania danych -

  • Fetch API (loadStudents_noCache) - Spowoduje to wyświetlenie nowego żądania za każdym razem, gdy kliknie kartę ucznia.

  • Apollo Client (loadWithApolloclient) - Spowoduje to pobranie danych z pamięci podręcznej.

Dodaj funkcję loadWithApolloclientktóry pyta o studentów i czas z serwera. Ta funkcja umożliwi buforowanie. Tutaj używamy funkcji gql do analizowania zapytania.

async loadWithApolloclient() {
   const query = gql`{
      getTime
      students {
         id
         firstName
      }
   }`;

   const {data} = await  client.query({query})
   return data;
}

Plik Fetch APIto prosty interfejs do pobierania zasobów. Funkcja Fetch ułatwia tworzenie żądań internetowych i obsługę odpowiedzi niż w przypadku starszego XMLHttpRequest. Poniższa metoda pokazuje ładowanie danych bezpośrednio za pomocą api pobierania -

async  loadStudents_noCache() {
      const response = await fetch('http://localhost:9000/graphql', {
      method:'POST',
      headers:{'content-type':'application/json'},
      body:JSON.stringify({query:`{
         getTime
         students {
            id
            firstName
         }
      }`})
   })

   const rsponseBody = await response.json();
   return rsponseBody.data;
}

W konstruktorze StudentsComponent wywołaj loadWithApolloClientmetoda. Kompletny Student.js plik jest poniżej -

import React, {Component} from 'react';
import { Link} from 'react-router-dom'

//Apollo Client
import {ApolloClient, HttpLink, InMemoryCache} from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
   link: new HttpLink({uri:`http://localhost:9000/graphql`}),
   cache:new InMemoryCache()
})

class Students extends Component {
   constructor(props) {
      super(props);
      this.state = {
         students:[{id:1,firstName:'test'}],
         serverTime:''
      }
      this.loadWithApolloclient().then(data => {
         this.setState({
            students:data.students,
            serverTime:data.getTime
         })
      })
   }
   
   async  loadStudents_noCache() {
      const response = await fetch('http://localhost:9000/graphql', {
         method:'POST',
         headers:{'content-type':'application/json'},
         body:JSON.stringify({query:`{
            getTime
            students {
               id
               firstName
            }
         }`})
      })
      const rsponseBody =  await response.json();
      return rsponseBody.data;
   }
   
   async loadWithApolloclient() {
      console.log("inside apollo client function")
      const query = gql`{
         getTime
         students {
            id
            firstName
         }
      }`;
      const {data} = await  client.query({query})
      return data;
   }
   
   render() {
      return(
         <div>
            <h3>Time from GraphQL server :{this.state.serverTime}</h3>
            <p>Following Students Found </p>
            <div>
               <ul>
                  {
                     this.state.students.map(s => {
                        return(
                           <li key = {s.id}>
                              {s.firstName}
                           </li>
                        )
                     })
                  }
               </ul>
            </div>
         </div>
      )
   }
}
export default Students

Krok 6 - Uruchom aplikację React z npm start

Możesz przetestować aplikację Reaguj, przechodząc z zakładki głównej do zakładki studenci. Po załadowaniu zakładki uczniów danymi z serwera. Będzie buforować dane. Możesz to przetestować, przełączając się z domu na kartę uczniów wielokrotnie. Wynik będzie taki, jak pokazano poniżej -

Jeśli najpierw załadowałeś stronę uczniów, wpisując adres URL, http://localhost:3000/#/students, widać, że czas ładowania aplikacji React i GraphQL byłby w przybliżeniu taki sam. Po tym, jeśli przełączysz się do widoku głównego i powrócisz do serwera GraphQL, czas się nie zmieni. To pokazuje, że dane są buforowane.

Krok 7 - Zmień wywołanie loadWithApolloclient na loadStudents_noCache

Jeśli zmienisz metodę ładowania na loadStudents_noCachew konstruktorze StudentComponent dane wyjściowe nie będą buforować danych. To pokazuje różnicę między buforowaniem a brakiem buforowania.

this.loadStudents_noCache().then(data => {
   this.setState({
      students:data.students,
      serverTime:data.getTime
   })
})

Z powyższego wyniku jasno wynika, że ​​jeśli przełączasz się między kartami, czas z serwera graphql zawsze będzie najpóźniejszy, co oznacza, że ​​dane nie są buforowane.