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.