Building web apps with dynamic APIs and server side rendering are a way to give people a great experience both with content and speed. How can we use Next.js to easily build those apps?

使用动态API和服务器端渲染来构建Web应用程序是一种使人们在内容和速度上都获得出色体验的方法。 我们如何使用Next.js轻松构建那些应用程序?

  • What are we going to build?

    我们要建造什么?

  • What is Next.js?

    什么是Next.js?

  • Step 0: Setting up a new Next.js app

    步骤0:建立新的Next.js应用程式

  • Step 1: Fetching Rick and Morty characters with an API in Next.js

    步骤1:在Next.js中使用API​​提取Rick和Morty字符

  • Step 2: Displaying Rick and Morty characters on the page

    步骤2:在页面上显示Rick和Morty字符

  • Step 3: Loading more Rick and Morty characters

    步骤3:载入更多Rick and Morty角色

  • Step 4: Adding the ability to search for Rick and Morty characters

    步骤4:新增搜寻Rick和Morty角色的功能

  • Step 5: Using dynamic routes to link to Rick and Morty character pages

    步骤5:使用动态路由链接到Rick和Morty角色页面

  • Bonus Step: Deploy your Rick and Morty wiki to Vercel!

    奖励步骤:将Rick and Morty Wiki部署到Vercel!

我们要建造什么? (What are we going to build?)

We’re going to have some fun and build out a web app that serves as a basic wiki for Rick and Morty characters.

我们将获得一些乐趣,并构建一个Web应用程序,作为Rick和Morty角色的基本Wiki。

Our app is going to consist of a few things:

我们的应用将包含以下几项内容:

  • A list of characters on the front page首页上的字符列表
  • A button that can load more characters, as the API is paginated在分页API时可以加载更多字符的按钮
  • A search box to look up characters搜索框以查找字符
  • A character page with basic details带有基本详细信息的字符页面

We’ll learn some concepts like:

我们将学习一些概念,例如:

  • How to spin up a web app with Next.js

    如何使用Next.js启动Web应用程序

  • How to fetch and use data from an API

    如何从API获取和使用数据

  • How to pre-render data from an API

    如何从API预渲染数据

  • How to set up dynamic routing

    如何设置动态路由

什么是Next.js? (What is Next.js?)

Next.js is a React framework from Vercel. It let’s you easily build lightweight dynamic web apps with a ton of modern features you’d expect out-of-the-box.

Next.js是Vercel的React框架。 它使您可以轻松地构建具有大量现成可用的现代功能的轻量级动态Web应用程序。

Vercel, the company that supports Next.js, is a service that allows you to automate continuous development pipelines to easily deploy web apps to the world. We’ll also use Vercel’s command line tool to optionally deploy our new wiki demo.

支持Next.js的公司Vercel是一项服务,可让您自动执行持续的开发流程,以轻松地将Web应用程序部署到全世界。 我们还将使用Vercel的命令行工具来选择性地部署我们的新Wiki演示。

步骤0:建立新的Next.js应用程式 (Step 0: Setting up a new Next.js app)

To get started, let’s spin up our Next.js project. We’ll use npm or yarn to get started:

首先,让我们启动Next.js项目。 我们将使用npm或yarn开始:

yarn create next-app
# or
npx create-next-app

Once you run that command, it will ask you a few questions. I’m going to call my project my-rick-and-morty-wiki, but you can name it whatever you’d like.

一旦运行该命令,它将询问您几个问题。 我将我的项目称为my my-rick-and-morty-wiki ,但是您可以根据需要命名。

It will then ask you which template to choose — go ahead and select the default template.

然后它将询问您要选择哪个模板-继续并选择默认模板。

Finally, it will install all of the dependencies.

最后,它将安装所有依赖项。

When it’s finished, you can navigate to that new directory and run:

完成后,您可以导航到该新目录并运行:

yarn dev
# or
npm run dev

You should now have a local server running at http://localhost:3000!

您现在应该在http:// localhost:3000上运行本地服务器!

步骤1:在Next.js中使用API​​提取Rick和Morty字符 (Step 1: Fetching Rick and Morty characters with an API in Next.js)

Now that we have our app set up, the first thing we need to actually build out our wiki is a list of characters.

既然我们已经设置了应用程序,那么实际构建Wiki所需的第一件事就是字符列表。

To do this, we’re going to start off in our home page in pages/index.js.

为此,我们将在我们的首页pages/index.js

Next.js scaffolds this page for us automatically. It’s the first page someone will hit on our website and has some basic features in the default template like a title, a simple grid, and some styles.

Next.js会自动为我们搭建此页面。 这是某人会在我们网站上访问的第一页,并且默认模板具有一些基本功能,例如标题,简单的网格和某些样式。

Currently, this page isn’t requesting any data. To get our characters, we’re going to jump right into requesting this server side.

当前,此页面未请求任何数据。 为了获得角色,我们将直接进入请求此服务器端的操作。

To do this, Next.js allows us to export an async getServerSideProps function right next to our page, which it will use to inject our page with any data that we fetch.

为此,Next.js允许我们在页面旁边导出一个异步getServerSideProps函数,它将使用该函数将获取的所有数据注入页面。

Let’s start off by adding the following snippet above our Home function component:

首先,在Home函数组件上方添加以下代码段:

const defaultEndpoint = `https://rickandmortyapi.com/api/character/`;export async function getServerSideProps() {const res = await fetch(defaultEndpoint)const data = await res.json();return {props: {data}}
}

Here’s what we’re doing:

这是我们正在做的:

  • We’re setting a variable called defaultEndpoint that simply defines our default API endpoint

    我们正在设置一个名为defaultEndpoint的变量,该变量仅定义了我们的默认API端点

  • We’re defining our getServerSideProps function that we’ll use to fetch our data

    我们正在定义用于获取数据的getServerSideProps函数

  • In that function, we first use the fetch API to make a request to our endpoint

    在该函数中,我们首先使用fetch API向端点发出请求

  • With it’s response, we run the json method so that we can grab the output in JSON format

    有了它的响应,我们运行json方法,以便我们可以获取JSON格式的输出

  • Finally, we return an object where we make our data available as a prop in the props property

    最后,我们返回一个对象,在该对象中,我们可以在props属性中将data作为prop

Now that we’re making that request, we need to make it available to use.

现在,我们正在发出该请求,我们需要使其可用。

Our data is made available as a prop, so let’s create an argument in our Home component function to grab that:

我们的data可以作为道具使用,因此让我们在Home组件函数中创建一个参数来抓取该参数:

export default function Home({ data }) {

To test this, we can use console.log to see the results:

为了测试这一点,我们可以使用console.log查看结果:

export default function Home({ data }) {console.log('data', data);

And once we save and reload the page, we can now see our results!

保存并重新加载页面后,我们现在可以看到结果!

Follow along with the commit!

跟随提交!

步骤2:在页面上显示Rick和Morty字符 (Step 2: Displaying Rick and Morty characters on the page)

Now that we have our character data, let’s actually display it on our page.

现在我们有了字符数据,让我们实际在页面上显示它。

To start, I’m going to make a few tweaks. I’m going to update:

首先,我将进行一些调整。 我要更新:

  • The <h1> title to “Wubba Lubba Dub Dub!”

    <h1>标题为“ Wubba Lubba Dub Dub!”

  • The <p> description to “Rick and Morty Character Wiki”

    “ Rick and Morty Character Wiki”的<p>描述

I’m also going to update the contents of <div clasName=“grid” to:

我还将将<div clasName=“grid”的内容更新为:

<ul className="grid"><li className="card"><a href="https://nextjs.org/docs"><h3>My Character</h3></a></li>
</ul>

What I’m doing here:

我在这里做什么:

  • I’m making the <div> a list as that will be better for accessibility

    我将<div>列为列表,因为这样会更好地实现可访问性

  • I’m making the <li> of the <ul> the card

    我正在制作<ul> card<li>

  • And just changing the <h3> to “My Character” temporarily

    并将<h3>临时更改为“我的角色”

To make sure our new <ul> doesn’t mess up the layout with it’s default styles, let’s also add the following to the bottom of the .grid CSS rules:

为了确保我们的新<ul>不会使用默认样式弄乱布局,我们还要在.grid CSS规则的底部添加以下内容:

list-style: none;
margin-left: 0;
padding-left: 0;

And now if we look at the page, we should see our basic changes.

现在,如果我们看一下页面,我们应该看到我们的基本更改。

Next, let’s make our grid load our characters.

接下来,让我们的网格加载角色。

At the top of our Home component function, let’s add:

在我们的Home组件函数的顶部,让我们添加:

const { results = [] } = data;

That will destructure our results array from our data object.

这将从我们的数据对象中破坏结果数组。

Next, let’s update our grid code:

接下来,让我们更新网格代码:

<ul className="grid">{results.map(result => {const { id, name } = result;return (<li key={id} className="card"><a href="#"><h3>{ name }</h3></a></li>)})}
</ul>

Here’s what we’re doing:

这是我们正在做的:

  • We’re using the map method to create a new list element for each of our results (or characters)

    我们正在使用map方法为每个结果(或字符)创建一个新的list元素

  • Inside of that, we’re grabbing the id and name from each  character result

    在其中,我们从每个字符结果中获取idname

  • We’re using the ID as a key for our list element to make React happy

    我们将ID用作列表元素的key ,以使React开心

  • We’re updating our header with the name

    我们将标题更新name

And once you save and reload the page, we should now see a new list of our characters from the API!

保存并重新加载页面后,我们现在应该从API中看到一个新的字符列表!

We can also add an image for each character.

我们还可以为每个字符添加一个图像。

First, inside of our grid, let’s update our destructure statement to grab the image URL:

首先,在网格内部,让我们更新分解结构语句以获取图像URL:

const { id, name, image } = result;

Next, let’s add the image above our header:

接下来,让我们在标题上方添加图片:

<img src={image} alt={`${name} Thumbnail`} />

And now each of our characters also shows their picture!

现在我们的每个角色也都展示了自己的照片!

Follow along with the commit!

跟随提交!

步骤3:载入更多Rick and Morty角色 (Step 3: Loading more Rick and Morty characters)

Now if you notice, when we load the page, we only get a certain number of results. By default, the API won’t return the entire list of characters, which makes sense, because it’s really long!

现在,如果您注意到,在加载页面时,我们只会得到一定数量的结果。 默认情况下,API不会返回整个字符列表,这很有意义,因为它真的很长!

Instead, it uses pagination and provides us with the “next” endpoint, or the next page of results, that will allow us to load in more results.

相反,它使用分页并为我们提供“下一个”端点或结果的下一页,这将使我们能够加载更多结果。

To start, we’re going to use React’s useState hook to store our results in state. We’ll then have the ability to update that state with more results.

首先,我们将使用React的useState挂钩将结果存储在状态中。 然后,我们将能够用更多结果更新该状态。

First, let’s import useState from React:

首先,让我们从React导入useState

import { useState } from 'react';

Next, let’s create our state by first renaming our original results variable and setting up our useState instance:

接下来,让我们通过首先重命名原始results变量并设置我们的useState实例来创建状态:

const { results: defaultResults = [] } = data;
const [results, updateResults] = useState(defaultResults);

If you save that and reload the page, you shouldn’t notice anything different yet.

如果保存并重新加载页面,则不会有任何不同。

Next, we want to be able to understand in our application what our current endpoint we’ve made a request is, what the next endpoint is, what the previous endpoint is, and how we can update all of that.

接下来,我们希望能够在应用程序中了解我们已请求的当前端点是什么,下一个端点是什么,上一个端点是什么以及如何更新所有端点。

To do this, we’re going to create more state. First, we want to update our destructuring statement with our data to get the info value:

为此,我们将创建更多状态。 首先,我们要使用data更新解构语句以获取info值:

const { info, results: defaultResults = [] } = data;

Next, let’s set up some state using that:

接下来,让我们使用它来设置一些状态:

const [page, updatePage] = useState({...info,current: defaultEndpoint
});

Here, we’re:

在这里,我们是:

  • Creating a new page state that we can use to get our prev and next values

    创建新page的状态,我们可以用它来获得我们的prevnext

  • We’re also creating a new value called current we’ll we start off by using our defaultEndpoint, which was the request made on the server

    我们还将创建一个名为current的新值,我们将使用defaultEndpoint ,这是在服务器上发出的请求

The idea here, is when we want to load more results, we’re going set up code to watch the value of current and update that value with the next, so when it  changes, we’ll make a new request.

这里的想法是,当我们想加载更多结果时,我们将设置代码以监视current的值,并使用next更新该值,因此当它更改时,我们将发出一个新请求。

To do that, let’s add a useEffect hook to make that request:

为此,我们添加一个useEffect挂钩来发出该请求:

const { current } = page;useEffect(() => {if ( current === defaultEndpoint ) return;async function request() {const res = await fetch(current)const nextData = await res.json();updatePage({current,...nextData.info});if ( !nextData.info?.prev ) {updateResults(nextData.results);return;}updateResults(prev => {return [...prev,...nextData.results]});}request();
}, [current]);

Here’s what’s going on:

这是怎么回事:

  • First, we’re destructuring the current value from `page

    首先,我们要从`page中解构current

  • We’re creating a useEffect hook that uses current as a dependency. If they value changes, the hook will run

    我们正在创建一个useEffect挂钩,该挂钩使用current作为依赖项。 如果它们的值更改,则挂钩将运行

  • If our current value is the same as defaultEndpoint, we don’t run the code, as we already have our request data. Prevents and extra on load request

    如果我们的current值与defaultEndpoint相同,则因为已有请求数据,所以我们不运行代码。 防止和额外的负载请求

  • We create an async function that we’re able to run. This allows us to use async/await inside of our useEffect hook

    我们创建一个能够运行的异步函数。 这使我们可以在useEffect挂钩中使用async/await

  • We make the request to the current endpoint. With that successful request, we update the page state with the new info like the new prev and next value

    我们向current端点发出请求。 与成功的请求,我们更新了page用新状态info ,如新prevnext

  • If our request does not have a previous value, that means it’s the first set of results for the given request, so we should completely replace our results to start from scratch如果我们的请求没有先前的值,则意味着它是给定请求的第一组结果,因此我们应完全替换结果以从头开始
  • If we do have a previous value, concatenate our new results to the old, as that means we just requested the next page of results如果确实具有先前的值,则将我们的新结果连接到旧结果,因为这意味着我们只请求了下一页结果

Again, if you save and reload the page, this still shouldn’t do anything and your page should be where it was before.

同样,如果您保存并重新加载页面,则此操作仍然不应执行,并且您的页面应位于之前的位置。

Finally, we’re going to create a Load More button and use it to update the current value to fire off a new request when we want a new page.

最后,我们将创建一个“加载更多”按钮,并使用它来更新current值,以在需要新页面时触发新请求。

To do that, let’s first add a new button below our grid:

为此,我们首先在网格下方添加一个新按钮:

<p><button>Load More</button>
</p>

Now we want something to happen when we click that button, so first add an event handler:

现在,我们希望在单击该按钮时发生某些事情,因此首先添加一个事件处理程序:

<button onClick={handleLoadMore}>Load More</button>

Then above the component return statement, let’s add that function:

然后在组件return语句上方,添加该函数:

function handleLoadMore() {updatePage(prev => {return {...prev,current: page?.next}});
}

When triggered with our button click, this function will update our page state with a new current value, specifically with the next value which is the endpoint to fetch our next page of results.

当单击按钮触发时,此函数将使用新的current值(特别是next值)来更新page状态, next值是获取下一页结果的端点。

And when we save and reload the page, it does just that!

当我们保存并重新加载页面时,它就完成了!

Follow along with the commit!

跟随提交!

步骤4:新增搜寻Rick和Morty角色的功能 (Step 4: Adding the ability to search for Rick and Morty characters)

One of the features out Rick and Morty API provides is the ability to filter results — so basically the ability to search. So let’s add that as a feature.

Rick and Morty API提供的功能之一是能够过滤结果-基本上就是搜索功能。 因此,我们将其添加为功能。

First, we need a search form. Let’s add the following snippet under the description paragraph:

首先,我们需要一个搜索表单。 让我们在description段落下添加以下代码段:

<form className="search"><input name="query" type="search" /><button>Search</button>
</form>

Next, let’s add these styles to the bottom of the first <style jsx> block:

接下来,让我们将这些样式添加到第一个<style jsx>块的底部:

.search input {margin-right: .5em;
}@media (max-width: 600px) {.search input {margin-right: 0;margin-bottom: .5em;}.search input,.search button {width: 100%;}
}

That’s going to give some spacing to our search input and button as well as make it mobile friendly. Feel free to add more styles if you’d like.

这将为我们的搜索输入和按钮留出一些空间,并使它易于移动。 如果需要,可以随意添加更多样式。

And if we save and refresh our page, we have a simple form.

而且,如果我们保存并刷新页面,我们将获得一个简单的表单。

It doesn’t do anything yet, so let’s make it search when submit the form.

它什么都没有做,所以让我们在提交表单时进行搜索。

To start, let’s add an onSubmit attribute to our form:

首先,让我们在表单中添加onSubmit属性:

<form className="search" onSubmit={handleOnSubmitSearch}>

And to go with that, let’s define our submit function above our return statement:

然后,让我们在return语句上方定义Submit函数:

function handleOnSubmitSearch(e) {e.preventDefault();const { currentTarget = {} } = e;const fields = Array.from(currentTarget?.elements);const fieldQuery = fields.find(field => field.name === 'query');const value = fieldQuery.value || '';const endpoint = `https://rickandmortyapi.com/api/character/?name=${value}`;updatePage({current: endpoint});
}

Here’s what we’re  doing:

这是我们正在做的:

  • First we’re preventing default behavior from the form submission to prevent the page from reloading首先,我们阻止表单提交的默认行为,以防止页面重新加载
  • Next we grab the current target, which is our form接下来,我们获取当前目标,即我们的表单
  • We grab the fields from the form by using the elements property. We also turn this into an array so it’s easy to work with我们使用elements属性从表单中获取字段。 我们还将其转换为数组,因此使用起来很容易
  • We search those fields for our query input我们在这些字段中搜索查询输入
  • We grab the value of that input我们获取该输入的价值
  • We create a new endpoint where we filter by name using that query value我们创建一个新的端点,在其中使用该查询值按名称进行过滤
  • Finally, we update our current property in our page state to trigger a new request to that endpoint

    最后,我们在页面状态下更新current属性以触发对该端点的新请求

And once you save that and reload the page, you can now give search a try. You should be able to type in a name like “rick”, hit enter or click the search button, and you should now see filtered results with the various ricks across the universe!

保存并重新加载页面后,现在可以尝试搜索。 您应该能够输入“ rick”之类的名称,按Enter或单击“搜索”按钮,现在您应该可以看到过滤后的结果以及整个Universe中的各种rick!

Follow along with the commit!

跟随提交!

步骤5:使用动态路由链接到Rick和Morty角色页面 (Step 5: Using dynamic routes to link to Rick and Morty character pages)

Now that we have all of our characters, we want to be able to click into those characters and display some additional details. To do that, we’re  going to make use of Next.js’s dynamic routes.

现在我们已经拥有所有字符,我们希望能够单击这些字符并显示一些其他详细信息。 为此,我们将利用Next.js的动态路由。

The first thing we need to do is properly configure our directory structure so Next.js recognizes the dynamic path. In order to set up a dynamic route, we need to create our folder exactly like:

我们需要做的第一件事是正确配置目录结构,以便Next.js识别动态路径。 为了设置动态路由,我们需要创建文件夹,如下所示:

- pages
-- character
--- [id]
-- index.js

Yes, that means you’re literally creating a folder with the name of [id], that’s not meant to be replaced. Next.js recognizes that pattern and will let us use that to create a dynamic route.

是的,这意味着您实际上是在创建一个名称为[id]的文件夹,而不是要替换该文件夹。 Next.js可以识别该模式,并让我们使用它来创建动态路由。

To make creating the page easier, we’re going to simply duplicate our homepage by copying our pages/index.js file into our next directly.

为了使创建页面更容易,我们将直接复制页面,方法是将pages/index.js文件直接复制到下一个pages/index.js

So we should now have a new page at pages/character/[id]/index.js.

因此,我们现在应该在pages/character/[id]/index.js有一个新页面。

Next, let’s remove a bunch of stuff so we can get to a good starting point:

接下来,让我们删除一堆东西,以便我们有一个好的起点:

  • Remove everything above the return statement in our page’s function component

    删除页面功能组件中return语句上方的所有内容

  • Rename the function component Character重命名功能组件字符
  • Remove the useState and useEffect imports

    删除useStateuseEffect导入

  • Remove the description, search form, grid, and load more button删除描述,搜索表单,网格并加载更多按钮
  • Optional: remove the footer可选:删除页脚

Once you’re done, the top of our page’s function component should look like:

完成后,页面功能组件的顶部应如下所示:

export default function Character({ data }) {return (<div className="container"><Head><title>Create Next App</title><link rel="icon" href="/favicon.ico" /></Head><main><h1 className="title">Wubba Lubba Dub Dub!</h1></main>

While there is some CSS we don’t need, we’re going to leave it all there for this demo. Feel free to clean some of that out later.

尽管有一些我们不需要CSS,但在本演示中将保留所有这些内容。 以后随时清理其中的一些。

If you navigate manually to /character/1, you should now see a simple page with just a title:

如果您手动导航到/ char / 1,现在您应该会看到一个只有标题的简单页面:

Next, let’s update the data we’re fetching. We can reuse most of the code in our getServerSideProps function.

接下来,让我们更新正在获取的数据。 我们可以在getServerSideProps函数中重用大多数代码。

We’re going to add a new argument to that getServerSideProps function:

我们将向该getServerSideProps函数添加一个新参数:

export async function getServerSideProps({ query }) {

When our page is rendered, Next.js injects data into our page and the getServerSideProps function about the environment. Here, we’re destructuring that data to grab the query object which will include any dynamic routing attributes, such as the [id] we’re setting in the route.

呈现我们的页面时,Next.js将数据注入到我们的页面以及有关环境的getServerSideProps函数中。 在这里,我们正在解构该数据以获取query对象,该query对象将包含任何动态路由属性,例如我们在路由中设置的[id]

Next, at the top of the getServerSideProps function, let’s destructure the ID:

接下来,在getServerSideProps函数的顶部,让我们getServerSideProps ID:

const { id } = query;

And finally let’s use that ID to dynamically create an endpoint we’ll use to fetch our character data:

最后,让我们使用该ID动态创建一个端点,以用于获取字符数据:

const res = await fetch(`${defaultEndpoint}${id}`);

Here, we’re using our character endpoint and appending the dynamic ID of our URL to the end of the URL.

在这里,我们使用字符端点,并将URL的动态ID附加到URL的末尾。

To test this out, let’s add a console.log to the top of the Character function:

为了测试这一点,让我们在Character函数的顶部添加console.log

export default function Character({ data }) {console.log('data', data);

And if we hit save and reload our page, we should now see the user details about character number 1 logged out, which is Rick Sanchez!

而且,如果单击保存并重新加载页面,现在我们应该已经注销了有关字符编号1的用户详细信息,这就是Rick Sanchez!

So we have the data, let’s add it to our page.

这样我们就有了数据,让我们将其添加到页面中。

At the top of the character function, let’s add this destructure statement:

在字符函数的顶部,让我们添加以下分解语句:

const { name, image, gender, location, origin, species, status } = data;

This gives us a bunch of attributes we’re getting right from that data object we saw logged out.

这为我们提供了一些属性,我们可以从看到的已注销数据对象中获取正确的属性。

To use that, we can start by updating the title to that name:

要使用它,我们可以先将标题更新为该名称:

<title>{ name }</title>

Also the <h1>:

也是<h1>

<h1 className="title">{ name }</h1>

At this point, we should now dynamically see Rick’s name.

在这一点上,我们现在应该动态地看到里克的名字。

Next, let’s add this block below our <h1> to include more of our character details:

接下来,让我们在<h1>下面添加此块,以包含更多字符详细信息:

<div className="profile"><div className="profile-image"><img src={image} alt={name} /></div><div className="profile-details"><h2>Character Details</h2><ul><li><strong>Name:</strong> { name }</li><li><strong>Status:</strong> { status }</li><li><strong>Gender:</strong> { gender }</li><li><strong>Species:</strong> { species }</li><li><strong>Location:</strong> { location?.name }</li><li><strong>Originally From:</strong> { origin?.name }</li></ul></div>
</div>

Here we’re using our characters image to display a picture of our character and other various metadata to add Character Details.

在这里,我们使用角色image来显示角色的图片以及其他各种元数据以添加角色详细信息。

We can follow that up by adding this snippet of CSS to our styles:

我们可以通过将以下CSS代码片段添加到样式中来进行后续操作:

.profile {display: flex;margin-top: 2em;
}@media (max-width: 600px) {.profile {flex-direction: column;}
}.profile-image {margin-right: 2em;
}@media (max-width: 600px) {.profile-image {max-width: 100%;margin: 0 auto;}
}

And now we have our character bio!

现在我们有了自己的角色简介!

So a quick recap, we have our new dynamic page. We can go to /character/1 or any ID to see a specific character. Let’s now update our homepage to link to these pages.

快速回顾一下,我们有了新的动态页面。 我们可以转到/character/1或任何ID来查看特定字符。 现在,让我们更新主页以链​​接到这些页面。

Back on pages/index.js, our homepage, let’s first import the Link component from Next.js:

回到我们的主页pages/index.js /index.js,让我们首先从Next.js导入Link组件:

import Link from 'next/link'

Next, inside of our grid where we map through our list of results, let’s use our <Link> component and update our code:

接下来,在我们通过结果列表进行映射的网格内部,让我们使用<Link>组件并更新代码:

<li key={id} className="card"><Link href="/character/[id]" as={`/character/${id}`}><a><img src={image} alt={`${name} Thumbnail`} /><h3>{ name }</h3></a></Link>
</li>

Here’s what we’re doing:

这是我们正在做的:

  • First we’re wrapping our <a> element with a <Link> component

    首先,我们用<Link>组件包装<a>元素

  • We add a href and the as properties to describe to Next.js what page we want to link to. We need to use the as property as it’s a dynamic link

    我们添加hrefas属性,以向Next.js描述我们要链接到的页面。 我们需要使用as属性,因为它是动态链接

  • We remove the href from our <a> element as it’s now being applied to the <Link> element

    我们将href<a>元素中删除,因为它已被应用于<Link>元素

If we save and reload our homepage, we’ll notice that nothing changed, but when we click any of our characters, we now go to their bio page!

如果我们保存并重新加载我们的主页,我们会注意到没有任何变化,但是当我们单击任何字符时,我们现在转到其履历页面!

Finally, let’s also add a button to our character bio page that links back to our homepage to make it easier to navigate.

最后,让我们在角色简介页面上添加一个按钮,该按钮可以链接回到我们的主页,从而更易于浏览。

First, let’s import the Link component:

首先,让我们导入Link组件:

import Link from 'next/link';

At the bottom of our <main> tag below our .profile div, let’s add this code:

.profile div下面的<main>标记的底部,让我们添加以下代码:

<p className="back"><Link href="/"><a>Back to All Characters</a></Link>
</p>

And we can add the following basic styles to simply make it look like a link:

我们可以添加以下基本样式以使其看起来像一个链接:

.back a {color: blue;text-decoration: underline;
}

And if we reload the page, we now have link that we can click to go back to the main page with all of our characters!

而且,如果我们重新加载页面,则现在具有链接,可以单击该链接以返回所有字符的主页!

Follow along with the commit!

跟随提交!

奖励步骤:将Rick and Morty Wiki部署到Vercel! (Bonus Step: Deploy your Rick and Morty wiki to Vercel!)

Because we’re using Next.js, Vercel makes it super simple to deploy our app.

由于我们使用的是Next.js,因此Vercel使部署我们的应用程序变得非常简单。

To do this, we need to install the Vercel CLI. We can do that by installing it as an npm module globally:

为此,我们需要安装Vercel CLI 。 我们可以通过将其作为npm模块全局安装来实现:

yarn global add vercel
# or
npm i -g vercel

Now, you  can run the vercel command in your terminal.

现在,您可以在终端中运行vercel命令。

The first time you run this, you’ll be prompted to log in. You’ll want to use your Vercel account to do this. If you don’t have one, you’ll want to sign up for a free account.

首次运行此程序时,系统将提示您登录。您将要使用Vercel帐户执行此操作。 如果您还没有,则需要注册一个免费帐户 。

With the Vercel CLI installed, we can simply run vercel in our project directory, fill out a few questions, and it will automatically deploy!

安装了Vercel CLI后,我们只需在项目目录中运行vercel ,填写一些问题,它将自动部署!

You can use pretty much all of the defaults, though you will probably need to use a different project name than I’m using.

您几乎可以使用所有默认值,尽管您可能需要使用与我使用的项目名称不同的名称。

But once finished, we now have successfully deployed our new app to Vercel!

但是,一旦完成,我们现在就可以成功地将新应用程序部署到Vercel!

我们还能做什么? (What else can we do?)

更多动态页面 (More dynamic pages)

Every time you make a request to a character, the API returns other endpoints that you can use such as locations and episodes. We can utilize those endpoints and create new dynamic pages, just like our dynamic character profile pages, to allow people to see more information about a specific location or episode.

每次您请求角色时,API都会返回您可以使用的其他端点,例如位置和情节。 就像我们的动态角色资料页面一样,我们可以利用这些端点并创建新的动态页面,以允许人们查看有关特定位置或情节的更多信息。

添加一些样式 (Add some styles)

We stuck with some of the basic styles that Next.js included and added some basic ones just for demonstration purposes. But now that you’re finished, you can have some fun and make it your own!

我们坚持使用Next.js包含的一些基本样式,并添加了一些仅用于演示目的的基本样式。 但是,既然您已经完成工作,就可以享受一些乐趣,然后自己创造一个!

添加字符过滤器 (Add character filters)

In addition to filtering by name, the API also supports filtering by status. By adding a status parameter to the endpoint URL, just like our name parameter, you can add a new filter to make it easier to find characters that are still alive or not.

除了按名称过滤外, API还支持按状态过滤 。 像我们的name参数一样,通过将status参数添加到端点URL,您可以添加一个新的过滤器,以使其更容易找到仍然存在的字符。