GraphQL - 快取



快取是指將資料儲存在稱為快取的臨時儲存區域中的過程。當您返回到最近訪問過的頁面時,瀏覽器可以從快取中獲取這些檔案,而不是從原始伺服器獲取。這樣可以節省您的時間,並減輕網路的額外流量負擔。

與 GraphQL 互動的客戶端應用程式負責在其端快取資料。一種可能的模式是保留一個欄位,例如 id,作為全域性唯一識別符號。

記憶體快取

InMemoryCache 是一個規範化的資料儲存,通常用於 GraphQL 客戶端應用程式,無需使用 Redux 等其他庫。

下面給出了使用 InMemoryCache 與 ApolloClient 的示例程式碼:

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

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

InMemoryCache 建構函式接受一個可選的配置物件,其中包含用於自定義快取的屬性。

序號 引數及描述
1

addTypename

一個布林值,用於確定是否將 __typename 新增到文件中(預設值:true)

2

dataIdFromObject

一個函式,接收一個數據物件並返回一個唯一識別符號,該識別符號將在將資料規範化到儲存區時使用

3

fragmentMatcher

預設情況下,InMemoryCache 使用啟發式片段匹配器

4

cacheRedirects

一個函式對映,用於在請求發生之前將查詢重定向到快取中的另一個條目。

圖示

我們將在 ReactJS 中建立一個單頁面應用程式,其中包含兩個選項卡 - 一個用於主頁選項卡,另一個用於學生。學生選項卡將從 GraphQL 伺服器 API 載入資料。當用戶從主頁選項卡導航到學生選項卡時,應用程式將查詢學生資料。生成的 資料將被應用程式快取。

我們還將使用getTime欄位查詢伺服器時間,以驗證頁面是否已快取。如果資料是從快取中返回的,則頁面將顯示傳送到伺服器的第一個請求的時間。如果資料是向伺服器發出的新請求的結果,它將始終顯示來自伺服器的最新時間。

設定伺服器

以下是設定伺服器的步驟:

步驟 1 - 下載並安裝專案所需的依賴項

建立一個資料夾cache-server-app。從終端將您的目錄更改為cache-server-app。按照環境設定章節中解釋的步驟 3 到 5 進行操作。

步驟 2 - 建立模式

在專案資料夾cache-server-app中新增schema.graphql檔案,並新增以下程式碼:

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

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

步驟 3 - 新增解析器

在專案資料夾中建立一個檔案resolvers.js,並新增以下程式碼:

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}

步驟 4 - 執行應用程式

建立一個server.js檔案。參考環境設定章節中的步驟 8。在終端中執行命令npm start。伺服器將在 9000 埠啟動並執行。在這裡,我們將使用 GraphiQL 作為客戶端來測試應用程式。

開啟瀏覽器並輸入 URLhttps://:9000/graphiql。在編輯器中鍵入以下查詢:

{
   getTime
   students {
      id
      firstName
   }
}

示例響應顯示了學生姓名和伺服器時間。

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

設定 ReactJS 客戶端

為客戶端開啟一個新的終端。在執行客戶端應用程式之前,應保持伺服器終端執行。React 應用程式將在 3000 埠執行,伺服器應用程式將在 9000 埠執行。

步驟 1 - 建立 React 應用程式

在客戶端終端中,鍵入以下命令:

npx create-react-app hello-world-client

這將安裝典型 React 應用程式所需的一切。npx 實用程式create-react-app工具建立了一個名為 hello-world-client 的專案。安裝完成後,在 VSCode 中開啟專案。

使用以下命令安裝 React 的路由模組 – npm install react-router-dom

步驟 2 - 啟動 hello-world-client

在終端中更改當前資料夾路徑到 hello-world-client。鍵入 npm start 以啟動專案。這將在 3000 埠執行開發伺服器,並自動開啟瀏覽器並載入索引頁面。

這在下面給出的螢幕截圖中顯示:

Screenshot Browser Launch Project

步驟 3 - 安裝 Apollo Client 庫

要安裝 Apollo Client,請開啟一個新終端並位於當前專案資料夾路徑中。鍵入以下命令:

npm install apollo-boost graphql

這將下載客戶端的 graphql 庫以及 Apollo Boost 包。我們可以透過鍵入 npm view apollo-boost dependencies 來交叉驗證這一點。這將具有許多依賴項,如下所示:

{ 
   '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' 
}

我們可以清楚地看到已安裝 apollo-client 庫。

步驟 4 - 修改 index.js 檔案中的 App 元件

對於簡單的 React 應用程式,您只需要保留src資料夾中的index.js和public資料夾中的index.html;所有其他自動生成的 檔案都可以刪除。

目錄結構如下所示:

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

新增一個額外的檔案students.js,它將包含 Students 元件。學生詳細資訊透過 Student 元件獲取。在 App 元件中,我們使用 HashRouter。

以下是 React 應用程式中的index.js

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"))

步驟 5 - 編輯 Students.js 中的元件 Students

在 Students 元件中,我們將使用以下兩種方法載入資料:

  • Fetch API (loadStudents_noCache) - 這將在每次單擊學生選項卡時觸發一個新請求。

  • Apollo Client (loadWithApolloclient) - 這將從快取中獲取資料。

新增一個函式loadWithApolloclient,該函式查詢伺服器中的學生和時間。此函式將啟用快取。這裡我們使用 gql 函式來解析查詢。

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

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

Fetch API是一個用於獲取資源的簡單介面。與舊的 XMLHttpRequest 相比,Fetch 使發出 Web 請求和處理響應變得更加容易。以下方法顯示了直接使用 fetch api 載入資料:

async  loadStudents_noCache() {
      const response = await fetch('https://: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;
}

在 StudentsComponent 的建構函式中,呼叫loadWithApolloClient方法。完整的Student.js檔案如下所示:

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:`https://: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('https://: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

步驟 6 - 使用npm start執行 React 應用程式

您可以透過在主頁選項卡和學生選項卡之間切換來測試 React 應用程式。一旦學生選項卡從伺服器載入了資料。它將快取資料。您可以透過多次在主頁和學生選項卡之間切換來測試它。輸出將如下所示:

Output React Application with NPM Start

如果您首先透過鍵入 URLhttps://:3000/#/students載入了學生頁面,您可以看到 React 應用程式和 GraphQL 的載入時間大致相同。之後,如果您切換到主頁檢視並返回到 GraphQL 伺服器,時間將不會改變。這表明資料已快取。

步驟 7 - 將 loadWithApolloclient 呼叫更改為 loadStudents_noCache

如果將 StudentComponent 建構函式中的載入方法更改為loadStudents_noCache,則輸出將不會快取資料。這顯示了快取和非快取之間的區別。

this.loadStudents_noCache().then(data => {
   this.setState({
      students:data.students,
      serverTime:data.getTime
   })
})
Output React Application with loadWithApolloclient

從以上輸出可以看出,如果您在選項卡之間來回切換,來自 graphql 伺服器的時間將始終是最新的,這意味著資料未被快取。

廣告