GraphQL教程——如何查询瑞克和莫蒂API

发布时间 2023-06-01 06:53:01作者: 晓风晓浪

在本文中,我们将使用 Apollo 客户端从以同名动画电视节目命名的Rick and Morty API中获取数据。我们将编写一个GraphQL 查询来获取我们需要的数据。然后将使用 React 显示数据。

在我们开始这个项目之前,让我们回顾一下 GraphQL 的用例,以及它与 REST API 的不同之处。

(React教程:https://www.java567.com/search.html?sWord=react&v=2306011

GraphQL 的用例是什么?

GraphQL 用于构建需要实时数据同步的应用程序,例如聊天应用程序。它允许开发人员获取必要的数据,减少网络上的数据传输,并提高应用程序性能。

微服务处理应用程序的特定功能或特性,这对开发人员单独使用多个 API 提出了挑战。

GraphQL 允许开发人员创建单个 API,作为通往众多微服务的网关。它还提高了性能,因为一个查询在单个请求中检索各种微服务。

GraphQL 提供了一种自文档化模式,使开发人员可以轻松理解数据模型和数据之间的关系。它还简化了创建、测试和维护 API 的过程,从而减少了时间和成本。

最后,GraphQL 提供了版本控制功能,允许在不破坏现有客户端的情况下改进 API 模式。版本控制是可能的,因为客户指定了他们需要的确切数据,这使得添加新字段和删除折旧字段变得容易,而不会影响现有客户。

GraphQL 和 REST API 之间有什么区别?

使用 GraphQL,客户端发送一个带有它需要的数据的查询,服务器只用该数据响应。另一方面,使用REST API,客户端向端点发送请求,服务器使用与端点相关的所有数据/响应进行响应。

REST API 是基于资源的,其中端点表示可以访问、创建、更新或删除的数据。另一方面,GraphQL 是基于图的,其中每个节点代表对象之间的关系。

REST API,以 JSON(JavaScript 对象表示法)或 XML(可扩展标记语言)格式返回数据。同时,GraphQL 允许客户端指定他们需要的数据,并用匹配查询的 JSON 对象进行响应。

GraphQL 提供版本控制以在不中断现有客户端的情况下实现 API 的发展,而 REST API 为每个版本创建新的端点。

在某些情况下,REST API 可能会出现过度获取或获取不足的情况,服务器可能会发送过多或较少的数据。GraphQL 通过允许客户端请求他们需要的数据来解决这个问题,从而减少通过网络传输的数据量。

项目设置

现在您已经熟悉了可以使用 GraphQL 做什么,让我们开始构建项目。

先决条件

  • React基础知识

  • 了解API和CSS(层叠样式表)的工作原理

依赖安装

创建一个名为“rickandmorty”的新 React App。

  npm init [React](https://react.dev/)-app rickandmorty 

或者

 npx create-[React](https://react.dev/)-app rickandmorty 

安装 Apollo 客户端和 GraphQL。下面的代码安装了两个依赖项:

  1. @apollo/client 包含您需要的一切,例如内存缓存、本地状态管理、错误处理和基于 React 的视图层。

  2. GraphQL 提供解析查询的逻辑。

 npm install @apollo/client [GraphQL](https://graphql.org/)

Rick & Morty API 和 Apollo 客户端设置

设置项目后,我们需要开始在我们的文件中使用它。接下来,index.js使用命令导航到您的文件cd,并添加以下代码:

 import [React](https://react.dev/)DOM from '[React](https://react.dev/)-dom/client';
 import './index.css';
 import App from './App';
 import reportWebVitals from './reportWebVitals';
 import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
 
 const client = new ApolloClient({
   uri: 'https://rickandmortyapi.com/[GraphQL](https://graphql.org/)',
   cache: new InMemoryCache(),
 });
 
 const root = [React](https://react.dev/)DOM.createRoot(document.getElementById('root'));
 root.render(
   <ApolloProvider client={client}>
   <App />
 </ApolloProvider>,
 );

上面的代码使用瑞克和莫蒂 API GraphQL 端点的 URL(统一资源定位器)创建了一个 Apollo 客户端实例。

App 组件与 Apollo 提供程序组件一起包装,以将客户端传递给所有子组件。

查询实现

characters.js现在,在文件夹内创建一个名为的文件src。该文件将包含查询和您要添加的任何其他函数。

在文件中,添加以下代码:

 import { gql } from '@apollo/client';
 
 export const GET_CHARACTERS = gql`
 query Characters{
     characters{
       results {
         name
         species
         status
         type
         gender
         origin{name}
         location {name}
         image
       },
     },
   }
 `;

在上面的代码中,我们gql从中导入@apollo/client来定义我们的查询。

我们创建变量并将其导出GET_CHARACTERS为带有大写字母的字符串。在 GraphQL 中定义查询时,大写被认为是最佳实践。使用模板文字包装字符串也被认为是最佳实践。

Javascript 中的对象是用键值对填充的集合或容器。键值对称为属性。

在我们的例子中,查询搜索瑞克和莫蒂中的角色。它返回一个具有该results属性的对象,该属性是一个字符对象数组。

每个角色都有名称、物种、状态、类型、性别和图像等属性——您可以选择要获取的内容?。

其他属性 origin 和 location 是具有每个角色的 origin 和 location 的 name 属性的对象。

字符函数定义

在该character.js文件中,修改后的查询下方添加以下代码,GET_CHARACTERS如下所示:

 import { useQuery, gql } from '@apollo/client';
 import { useState } from "[React](https://react.dev/)";
 import { RandomCharacter } from './randomcharacters';
 import './App.css';
 
 export const GET_CHARACTERS = gql`
 query Characters($name: String){
     characters ( filter: {name: $name}){
       results {
         name
         species
         status
         type
         gender
         origin{name}
         location {name}
         image
       },
     },
   }
 `;
 
 export function CharacterList() {
   const [searchTerm, setSearchTerm] = useState("");
   const {loading, error, data }   = useQuery(GET_CHARACTERS, {variables: {name: searchTerm}});
   const handleChange = (event) => {
     setSearchTerm(event.target.value);
  };
   return (
     <div>
       <input type="text" name="search" placeholder="search for Rick and Morty characters..." value={searchTerm} onChange={handleChange} className="search-input"  />
      {loading && (
             <div className="loader-container">
               <div className="loader"></div>
             </div>
        )}
      {error && <p> error </p> }
      {data?.characters.results.length === 0 && (<>   <RandomCharacter/> </>)}
      {data && data.characters.results.map((character) => (
         <div className="card" key={character.name} style={{ backgroundImage: `url(${character.image})`,backgroundRepeat: 'no-repeat'}}>
           <div className="info">
           <h2 className="h3"> {character.name}</h2>
           <p> Status: {character.status}</p>
           <p> Species: {character.species} </p>
           <p> Type: {character.type}</p>
           <p> Gender: {character.gender}</p>
           <p> Origin: {character.origin.name}</p>
           <p> Location: {character.location.name}</p>
         </div>
         </div>
       ))}
     </div>
   );
 }

创建export function CharacterList()一个函数,该函数也导出到代码的其他部分并且可以被代码的其他部分使用。

searchTerm变量将搜索状态初始化为空字符串,并创建一个函数setSearchTerm来更新值。

useQuery来自库的钩子从@apollo/clientAPI 获取数据。

查询传递GET_CHARACTERS一个名为searchTermwhich 的变量,该变量用于保存要搜索的字符名称。

handleChange 变量将searchTerm的值设置为输入字段的当前值。该input字段是用户将用于搜索他们想要查看的字符的名称的搜索栏。状态由 处理handleChange

我们还需要考虑加载站点的问题,以及可能发生的任何错误。

loading如果加载设置为 ,则使用微调器动态呈现True。如果将错误设置为 ,则会显示一条错误消息True

当用户正在搜索一个不存在的角色时,我们想要显示一条消息和一个他们可以找到更多信息的替代角色——这就是进来的地方。我们稍后会定义它RandomCharacter。现在,让我们保持原样。

获取数据后,我们将data.characters.results数组映射到每个角色的卡片。

我们还想更改卡片的背景以代表信息所针对的角色。属性backgroundImage中的style处理图像的动态变化。其余项目在卡片上显示为文本。

如何显示数据

现在我们有一个功能可以工作,我们需要查看浏览器中显示的内容,以及我们是否可以进行查询并获取我们需要的数据。

在您的App.js文件中,添加以下代码:

 function App() {
  return (
    <div>
    <h1 style={{ textAlign: 'center' }} >Rick and Morty Characters</h1>
    <CharacterList />
  </div>
      );
    }

<CharacterList />组件显示有关我们从 API 获取的字符的信息。

如何随机化字符

请记住,我们调用了RandomCharacter组件,但尚未定义它。

randomcharacters.js创建一个调用的文件src并添加以下代码:

 import { useQuery } from "@apollo/client";
 import { gql } from '@apollo/client';
 import { useState } from "[React](https://react.dev/)";
 import './App.css';
 
 export const GET_SINGLE_CHARACTER = gql`
 query Character($id: ID!){
    character   (id: $id) {
        name
        species
        status
        type
        gender
        origin{name}
        location {name}
        image
      },
    },
 `;
 
 export const RandomCharacter = () => {
    const [randomNumber, setRandomNumber] = useState(Math.floor(Math.random() * 200));
    const { loading, error, data } = useQuery(GET_SINGLE_CHARACTER, {variables: {id: randomNumber } });
     
 
  return (
    <div>
        <p className="intro" >
          Sorry, we couldn't find that character ?  
          <br/>
          <br/>
          How about this one instead? ? </p>
 
      {/* {loading && <p>loading...</p>} */}
      {loading && (
            <div className="loader-container">
              <div className="loader"></div>
            </div>
        )}
      {error && <p> error </p> }
      {data && (<>
        <div className="card" key={data.character.name} style={{ backgroundImage: `url(${data.character.image})`,backgroundRepeat: 'no-repeat'}}>
        <div className="info">
          <h2 className="h3">{data.character.name}</h2>
          <p>Status: {data.character.status}</p>
          <p>Species: {data.character.species}</p>
          <p>Type: {data.character.type}</p>
          <p>Gender: {data.character.gender}</p>
          <p>Origin: {data.character.origin.name}</p>
          <p>Location: {data.character.location.name}</p>
        </div>
        </div>
      </>
      )}
    </div>
  );
 };

我们将复制我们在文件中创建的查询characters.js,并将其重命名为GET_SINGLE_CHARACTER. 我们将寻找 s 而不是寻找名字ID

我们寻找IDs 是因为它们是唯一的,并且我们希望在用户没有找到他们正在寻找的字符时随机选择字符。

randomNumber将状态初始化为Math.floor生成 0 到 199 之间的随机数的函数,使用该Math.random()方法并将其乘以 200。

Math.floor函数将表达式的结果四舍五入为最接近的整数。每次randomNumber需要更新时,该 setRandomNumber函数都会将新值作为其参数并更新状态。

我们有一条消息提醒用户他们正在寻找的角色没有找到,但他们可以签出一个新角色。

加载微调器也在此组件中实现,如果出现任何问题,错误就在那里。图像和卡片类似于格式,characters.js因为我们希望与所有内容的外观保持一致。

如何设置显示样式

我们将使用 CSS 来设置卡片的外观样式,以及搜索栏和一般页面。

定义了功能和组件后,我们将向className需要设置样式的内容添加属性。

添加以下代码:

 @import url(https://fonts.googleapis.com/css?family=Roboto:400,500,700);
 body{
   background: navajowhite;
   font-family: Roboto, veranda;
   padding-bottom: 4em;
 }
 .card{
   position: relative;
   width: 22em;
   height: 30em;
   background-size: 22em 30em;
   box-shadow: 3px 3px 20px rgba(0,0,0,0.5);
   margin: auto;
   overflow: hidden;
   margin-bottom: 2em;
 }
 .card *{
   position: relative;
   z-index: 2;
 }
 .card:hover .info{
   bottom: -3em;
   opacity: 1;
   padding: 2px 1px;
   background-color: navajowhite;
 }
 .info{
   font-family: 'Droid Serif', serif;
   font-size: 1.2em;
   color: black;
 
   line-height: 1.1em;
   padding: 0 2em;
   position: relative;
   bottom: -4em;
   opacity: 0;
   background: transparent;
   transition: opacity 0.3s, bottom 0.3s;
   text-align: center;
 }
 /* search bar*/
 input[type="text"] {
   border: none;
   border-radius: 10px;
   background-color: #f2f2f2;
   padding: 10px;
   width: 500px;
   margin: 0 auto;
   display: block;
   font-size: 16px;
   font-family: 'Roboto', sans-serif;
   box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
   margin-bottom: 2em;
 }
 
 input[type="text"]::placeholder {
   color: #999;
   font-style: italic;
 }
 /* no result */
 .intro{
 /* width: 10px; */
 text-align: center;
 margin: 0 auto;
 color:black;
 font-family: 'Droid Serif', serif;
 font-size: 23px;
 font-style: italic;
 line-height: 20px;
 padding-bottom: 15px;
 }
 /* spinner */
 .loader-container {
   display: flex;
   justify-content: center;
   align-items: center;
   height: 100px;
 }
 
 .loader {
   border: 8px solid #f3f3f3;
   border-top: 8px solid black;
   border-radius: 50%;
   width: 50px;
   height: 50px;
   animation: spin 1s linear infinite;
 }
 
 @keyframes spin {
   0% { transform: rotate(0deg); }
   100% { transform: rotate(360deg); }
 }

注意事项:

  • 该类.card表示卡片的外观。

  • 该类.info表示字符的正文文本,例如物种。

  • 该类.intro表示未找到该字符时出现的文本。

  • 该类.loader表示在显示结果之前显示的微调器。

结论

在本文中,您学习了如何将 GraphQL 查询与 React 结合使用,如何使用 useState 挂钩管理状态,以及如何设置 Web 应用程序不同组件的样式。

(SQL教程:https://www.java567.com/search.html?sWord=sql&v=2306011