使用 Next.js 有很多好处,但一个非常有用的特性是它的文件路由系统。这种架构显着简化了在网站内创建路由的过程。在本教程中,我们将学习如何设置 Next.js 项目以及 Next.js 中的文件路由系统如何工作。
我们还将学习如何:
……还有更多。
我们将通过构建一个投资组合页面来了解所有这些。
Next.js 是一个基于 React 的 Web 框架,构建在 Node.js 之上。由于它基于 React,它也遵循组件架构设计。
Next.js 可用于构建静态站点。这些网站的页面会在构建时预先呈现并提供给用户。简而言之,该页面甚至在用户请求之前就可用。
它还允许使用服务器端渲染来生成动态网页(每次用户发出新请求时都会更改的页面)。
Next.js 中的路由架构使得创建和链接页面变得非常容易。我们可以动态传递查询参数,并创建动态页面,而无需离开pages文件夹。
Next.js 框架于 2016 年 10 月 25 日首次发布。从那时起,出于几个原因,它继续成为最受欢迎的 Web 框架之一。
一方面,Next.js 本质上是 React。这对于来自 React 背景的庞大开发者社区来说是个好消息。开发人员仍然可以使用一些 React 特性,比如组件架构、JSX 等等。
其次是使用 Next预渲染页面的能力。默认情况下,Next.js 会提前生成所有页面,然后在每个用户请求中重复使用这些页面。由于该站点是预先生成的,因此搜索引擎爬虫可以正确索引该站点以进行 SEO。
如前所述,Next.js 中的文件路由系统是一个非常有用的功能,它显着简化了在网站内创建路由的过程。.js所以你基本上可以在一个名为 的文件夹中创建一堆文件pages,Next.js 将用于你的所有路由。它不仅有用,而且非常强大。
该站点将有两条简单的路线和两条动态路线(稍后我们将了解它们的含义)。
该Projects页面将呈现项目列表。单击查看更多后,我们将被定向到单个项目页面。
该页面将呈现博客文章列表,我们也可以通过单击阅读更多blog来查看单个博客页面。该页面将包含有关特定帖子的详细信息。
为了在 Next 中演示路由嵌套,我们还将/comments为每篇博文创建一个嵌套路由。例如,我们可以通过访问来查看第一篇文章的评论localhost:3000/blog/first-post/comments。
这是该项目的实时预览:
您可以从其GitHub 存储库中获取代码,在您的机器上运行它并根据需要对其进行调整。您可以删除我的图像并将您的图像移动到/public文件夹中。您只需要更改标记中的文件名。
要开始使用 Next,您需要在计算机上安装 Node.js。Node 的版本不应低于12.x. node -v您可以通过在命令终端上键入来检查版本。
如果你没有安装 Node.js,或者有旧版本,你可以从这里下载最新版本。
下载后,我们需要初始化我们的项目。我们可以自动或手动执行此操作。在本教程中,我们将使用create-next-app代码生成器自动为我们构建一个工作框架。
请导航到您希望项目所在的文件夹并输入以下命令:
cd your/path
npx create-next-app next-portfolio
最后,运行以下命令:
npm run dev
如果一切顺利,您应该在终端窗口上看到以下内容。
我们可以在 web 浏览器上的http://localhost:3000查看页面。
当我们运行该命令时,我们在next-portfolio当前目录中创建了一个名为的文件夹。在里面next-portfolio,你会发现一些重要的文件夹和文件。
我们最常使用的文件夹是pages. 在 Next 中,.js内部定义的每个文件都pages映射到一个类似命名的路由:
这是典型 Next 项目中 pages 文件夹的高级表示:
my-site
└── pages
└── api // API routes
├── _app.js // custom route (will **not** be used as a route)
├── index.js // index route (will be rendered at my-site.com)
├── about.js // predefined route (will be rendered at my-site.com/about)
├── contact.js // predefined route (will be rendered at my-site.com/contact)
└── blog
├── index.js // index route for blog (will be rendered at my-site.com/blog)
├── author.js // predefined route (will be rendered at my-site.com/blog/author)
├── [blog-id].js // handles dynamic route (will render dynamcially, based on the url parameter)
└── [...slug].js // handles catch all route (will be rendered at all routes following my-site.com/blog)
每个 React 组件都将捆绑为一个.js文件,其中包含每个页面的标记和逻辑。
Next.js 提供了一个公共文件夹,您可以在其中存储图像、自定义脚本和字体等静态资产,并从您的组件/代码中引用它们。
我们将在投资组合网站的各个页面中使用以下图片:
您可能已经注意到_app.js页面文件夹中的页面。此页面是自定义页面。Next.js不将自定义页面用作路由,它们的前缀是下划线 ( _)。
Next.js 使用_app.js来初始化网页。该组件初始化应用程序并传递pagePropsprop,这是我们网站中所有嵌套组件所需的数据。
作为根组件,我们可以定义一个我们想要在所有页面中持久化的布局。
我们还可以使用适用于所有元素的全局样式表,如下例所示:
//next-portfolio/pages/_app.js
import Layout from '../components/Layout'
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp
my-site.com每当我们导航到诸如、my-site.com/blog或之类的索引路由(也称为主页)时my-site.com/projects,Next.js 都会从该目录中读取名为index.js.
所以本质上,pages/index.js返回主页的标记,显示在localhost:3000. pages/blog/index.js返回博客主页的标记,位于localhost:3000/blog.
在您的代码编辑器中,请转到索引页面并删除所有文件内容。以下标记用于测试目的:
// next-portfolio/pages/index.js
import Image from 'next/image'
import Link from 'next/link'
export default function Home() {
return (
<div className="container">
<h1>Hello World</h1>
</div>
)
}
注意:进入next-portfolio/styles您的文本编辑器,然后删除Home.module.css,因为我们根本不需要它。
现在保存文件更改并打开http://localhost:3000。索引文件中的更改将反映在索引路由上。
文件中将包含更多内容index.js。主页的顶部将包含导航链接。但是,在主页之前构建其他页面更直观,因此我们可以正确链接到它们。
出于这个原因,我们需要先创建一些其他页面,然后再构建我们的主页。
首先,我们将为我们的投资组合网站创建两条静态路由。这些路由呈现静态数据:它们不使用 URL 中的查询参数来呈现数据。
我们将创建的两个静态路由是about.js和contact.js。这些文件将分别用于/about和/contact路由。
为此,请导航到next-portfolio/pages并创建一个名为about.js. 关于页面的标记将进入其中:
// next-portfolio/pages/About.js
export default function About() {
return (
<div className="container">
<h1> About me </h1>
<p> My name is Kingsley Ubah. I'm a 22-year-old web developer from Nigeria. I'm particularly interested in technical writing. When I'm not coding or writing, I read my favorite books and play some cool video games. I'm a huge fan of good movies and football. Also, don't play with my food!</p>
<p>I'm skilled in front-end web development. I'm equally good at the back end. Technologies I work well with include React, Node.js, Vue, Next, Gatsby, OAuth, MongoDB, MySQL and many others. </p>
<p>I could keep going on and on about my life but I can guarantee that you'll be bored in the end. So I'll just end it right here.</p>
</div>
)
}
注意:当然,如果您愿意,您可以根据自己的技能自定义内容!
现在请保存文件更改,转到next-portfolio/styles/globals.css并输入以下样式:
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300&display=swap');
html,
body {
padding: 0;
margin: 0;
font-family: "lato", sans-serif;
font-size: 20px;
background-color: #D7E5f0;
}
* {
box-sizing: border-box;
}
h1 {
font-size: 60px;
}
.logo {
font-weight: 600;
font-size: 30px;
}
p {
font-size: 20px;
font-weight: 600;
line-height: 1.2;
}
a {
text-decoration: none;
color: black;
}
.container {
margin: 0 auto;
max-width: 1200px;
}
注意:再一次,如果你想要一套不同的风格,那就去城里吧。
保存更改。在您的网络浏览器上,请导航到http://localhost:3000/about。
最后,对于静态页面,请contact.js在其中创建一个文件pages并创建Contact组件,如下所示:
// next-portfolio/pages/Contact.js
import Image from 'next/image'
export default function Contact() {
return (
<div className="container">
<h1> Contact me </h1>
<p> I'd love to hear from you. Want to reach out, you can contact me on the
following profiles</p>
<ul className="contact">
<div className="link">
<li>
<Image src='/facebook.png' height={20} width={20} />
<a href='https://facebook.com/UbahTheBuilder'> Like me on Facebook</a>
</li>
</div>
<div className="link">
<li>
<Image src='/twitter.png' height={20} width={20} />
<a href='https://twitter.com/UbahTheBuilder'> Follow me on Twitter</a>
</li>
</div>
<div className="link">
<li>
<Image src='/linkedin.png' height={20} width={20} />
<a href='https://linkedin.com/UbahTheBuilder'> Connect with me on LinkedIn</a>
</li>
</div>
<div className="link">
<li>
<Image src='/whatsapp.png' height={20} width={20} />
<a href='https://whatsapp.com/UbahTheBuilder'> Chat with me on Whatsapp</a>
</li>
</div>
</ul>
<form>
<input type="text" placeholder="your name" />
<br />
<input type="email" placeholder="your email address" />
<br />
<input type="text" placeholder="subject" />
<br />
<textarea id="message" rows="15" cols="65" placeholder="your message"></textarea>
<br />
<input type="submit" value="Reach out" />
</form>
</div>
)
}
从这个组件中,我们返回一个包含社交媒体链接的页面,以及一个联系表格。
对于链接,您会注意到我们导入并使用了next/imageImage提供的组件 。
该Image组件有助于创建更好的优化和响应式图像,这些图像随着浏览器窗口的大小而缩放。
为了更好地设置样式,请随意复制以下样式并将它们粘贴到全局样式表中:
/* next-portfolio/styles/globals.css */
/* CONTACT PAGE */
.link {
width: 500px;
display: flex;
justify-content: space-between;
align-items: center;
margin: 5px 0;
font-size: 17px;
}
input {
height: 50px;
width: 500px;
margin: 10px 0;
font-size: 17px;
padding-left: 3px;
}
input[type=submit] {
background-color: blue;
color: white;
border: none;
}
请保存文件更改并导航到http://localhost:3000/contact。
构建页面是一个过程。用户还必须能够在这些页面之间导航。四个页面中的两个已经实现,现在让我们完成主页。首先,我们进入索引文件并修改Home组件,如下所示:
// pages/index.js`
import Image from 'next/image'
import Link from 'next/link'
export default function Home() {
return (
<div className="container">
<div className="navbar">
<div className="logo">Pragmatic Developer</div>
<ul>
<li>
<Link href="/about">
<a>About me</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>Contact me</a>
</Link>
</li>
<li>
<Link href="/blog">
<a>Blog</a>
</Link>
</li>
<li>
<Link href="/projects">
<a>Projects</a>
</Link>
</li>
</ul>
</div>
<div className="profile">
<Image src="/me.png" height={200} width={200}
<div className="intro">
<h1>Hi, I'm Kingsley</h1>
<p>I am a web developer and technical writer</p>
</div>
</div>
</div>
)
}
如果您曾经在 React 应用程序中实现过客户端路由,您可能会熟悉React RouterLink中的 React组件。
Next.js 也为我们提供了一个类似的组件,我们从next/link导入。
该<Link>组件用于在 Next 应用程序中实现页面转换。这个组件最大的特点是它允许你将查询参数传递给useRouter,这是你用来在动态路由上呈现内容的东西。
在 JSX 标记内部,我们注册组件并传入有效href属性,指定我们希望从导航菜单链接到的页面。
该组件还可以接收几个属性,其中一些将在以下部分中显示。
有时您可能想要使用自定义 URL,可能是为了使 URL 更具可读性和语义。
为此,您可以将as属性传递给Link,如下所示:
<ul>
<li>
<Link href="/about" as="/king">
<a>About me</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>Contact me</a>
</Link>
</li>
<li>
<Link href="/blog">
<a>Blog</a>
</Link>
</li>
<li>
<Link href="/projects">
<a>Projects</a>
</Link>
</li>
</ul>
我确实提到 Next.js 作为一个框架允许我们预渲染页面。此属性使我们能够预先获取在后台呈现 About 页面所需的资源:
<Link href="/about" prefetch>
<a>About me</a>
</Link>
现在保存文件。随意在您的全局样式表中使用以下样式:
/* next-portfolio/styles/globals.css */
/* HOME PAGE */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
}
.navbar ul {
display: flex;
}
.profile {
display: flex;
max-width: 900px;
margin: 180px auto;
}
li {
list-style-type: none;
}
.navbar a {
text-decoration: none;
color: rgb(59, 58, 58);
margin: 0 25px;
transition: 0.2s;
}
.navbar a:hover {
background-color: blue;
color: white;
padding: 8px 8px;
border-radius: 6px;
}
.intro {
margin: 0 90px;
}
.contact a {
margin: 0 15px;
}
将样式保存到文件并在 Web 浏览器上导航到http://localhost:3000 。
单击导航菜单上的联系人后,您会发现我们现在可以从主页移动到联系人页面。
在 Next.js 中,动态路由是动态呈现内容的特殊路由,具体取决于id来自 URL 的查询。
[param].js动态路由由使用约定定义的特殊文件处理。param从查询对象中获取。
因此,不要为不同的路由定义不同的文件,如:
…我们可以定义一个动态页面来处理任何动态路由/blog:
每当导航到上述任何 URL 时,如下所示:
<li><Link href="/blog/1"><a>Visit my first post</a></Link></li>
// 1 is the blog-id which will get sent to the dynamic component
first-post... 在动态组件中,我们可以从 URL访问查询 ID(即 1、2、3、 ...)。
我们通过导入和调用useRouter() 钩子来做到这一点。然后我们从对象中解构param值router并根据它决定渲染什么。
因此,如果您blog/1从主页导航到,:id可以像这样获得 of 1:
import {useRouter} from 'next/router'
export default function Blog() {
const router = useRouter();
const {id} = router.query;
return (
<div className="container">
<h1> You are now reading article {id} </h1> // You are now reading article 1
</div>
)
}
您还可以使用查询字符串而不是完整的 URL 路径:
<li><Link href="/blog?title=my-first-post"><a>Visit my first post</a></Link></li>
注意:通常,您会使用查询 ID 查询数据库,然后检索将显示在动态页面上的匹配数据记录。在本教程中,我将使用模拟 JSON 数据来简化一切。
第一个动态页面将用于项目。
在里面pages,创建一个名为projects. 然后在新文件夹中,创建一个名为index.js.
该文件将返回当我们在 Web 浏览器上查看http://localhost:3000/projects时显示的内容。换句话说,这将是/projects.
我们还需要一些用于项目的模拟 JSON 数据。在里面pages,创建一个名为projects.json. 然后创建一个您自己的项目数组,如下所示:
// next-portfolio/pages/projects.json
[
{
"id": 1,
"cover": "https://uploads.sitepoint.com/wp-content/uploads/2021/10/1633599028SkilllzLanding.png",
"title": "Skilllz",
"slug": "projects/first-project",
"excerpt": "A Sleek Purple Landing Page For an online training platform. Learn some important CSS concepts by building a landing page"
},
{
"id": 2,
"title": "Movie Generator App",
"cover": "https://uploads.sitepoint.com/wp-content/uploads/2021/10/1633599458moviegenerator.png",
"slug": "projects/second-project",
"excerpt": "Learn how to build CRUD applications with React and HarperDB. This in depth tutorials covers a lot about API integartion"
},
{
"id": 3,
"title": "Hacker News Clone",
"cover": "https://uploads.sitepoint.com/wp-content/uploads/2021/10/1633599423hackernewsclone.png",
"slug": "projects/third-project",
"excerpt": "Have you always wanted to clone a web page? Build a Hacker News Clone using React and HarperDB. Get started with it now"
}
]
JSON 包含我们想要在http://localhost:3000/projects显示的项目数据。
之后,我们会将这些数据带入标记中,如下所示:
// next-portfolio/pages/projects/index.js
import Portfolios from '../projects.json'
import Link from 'next/link'
export default function Projects() {
return (
<div className="container">
<h1> My Projects </h1>
<div className="projects">
{Portfolios.map(portfolio => {
return(
<div className="project" key={portfolio.id}>
<img src={portfolio.cover} <h2>{portfolio.title}</h2>
<p>{portfolio.excerpt}</p>
<Link href={portfolio.slug}><a>View More</a></Link>
</div>
)}
)}
</div>
</div>
)
}
我们做的第一件事是导入数据。map()然后我们使用 JavaScript函数将每个项目映射到 JSX 模板中。
我们还需要让它更美观,所以请随意使用以下样式:
// next-portfolio/styles/globals.css
/* PROJECTS */
.projects {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.project img {
height: 100px;
width: 200px;
}
.project a {
color: white;
background-color: black;
padding: 10px 10px;
border-radius: 6px;
}
.project {
max-width: 500px;
background-color: blue;
border-radius: 6px;
color: white;
padding: 30px 30px;
margin: 30px 0;
}
要在浏览器上查看页面,请导航到http://localhost:3000/projects。
现在,我们需要实现显示单个项目的动态路由。因此,如果我们导航到http://localhost:3000/projects/1,将显示第一个项目。
在项目文件夹中pages,创建一个名为[project].js.
该文件将为单个项目呈现动态页面,例如 onprojects/1等projects/2。
在文件中,我们定义了将用于单个项目页面的模板,如下所示:
// next-portfolio/pages/projects/[project].js
import {useRouter} from 'next/router'
export default function Project() {
const router = useRouter();
const {project} = router.query;
return (
<div className="container">
<div>
<h1>This is the {project}</h1>
<p>Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
)
}
注意:从路由器对象中,我们从查询对象中获取查询 ID。通常,您会使用该密钥对匹配记录进行 API 查询。这样,您还可以在数据库中找不到匹配项目的情况下显示错误消息。
由于我们没有用于查询项目的 API,我们改为显示 URL slug 以及一些静态lorem ipsum文本。slug 标识渲染的页面。
下图显示了页面如何根据 URL 进行更改。
例如,让我们考虑一个博客。当用户导航到 时my-site.com/blog,会显示博客文章列表。
当用户导航到 时my-site/blog/first-post,将显示第一篇博文。当他们导航到 时my-site/blog/first-post/comments,将有与第一篇文章相关的所有评论。这称为路由嵌套。
在 Next.js 中,您还可以嵌套动态路由。每个子路由都可以访问:id父路由的查询。这样,my-site.com/blog/first-post/comments将不同于,比如说,my-site.com/blog/second-post/comments因为您可以:id从 URL 或查询对象中检查帖子,使用useRouter().
事实上,我们将对我们的博客页面做类似的事情。每篇博文都有自己的一组评论。换句话说,我们将[comments].js在另一个名为 的动态页面中嵌套一个名为 的动态页面[blog].js。
在进入路由嵌套之前,我们将首先创建博客主页。
为此,请 cd 进入next-portfolio/pages并创建一个名为blog. 在新文件夹中,创建一个名为index.js.
该文件将返回在http://localhost:3000/blog 上显示的内容。换句话说,它是该路线的主页。
接下来,我们为博客文章创建数据:
// next-portfolio/pages/posts.json
[
{
"id": 1,
"cover": "https://uploads.sitepoint.com/wp-content/uploads/2021/10/1633515082detectcanva.png",
"title": "How to detect the operating system in React and Render accordingly",
"slug": "blog/first-post",
"excerpt": "Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
"body": "Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
},
{
"id": 2,
"title": "Learn all about the JavaScript reduce method",
"cover": "https://uploads.sitepoint.com/wp-content/uploads/2021/10/1633515150jsreduce.png",
"slug": "blog/second-post",
"excerpt": "Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
"body": "Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
},
{
"id": 3,
"title": "Understanding React props",
"cover": "https://uploads.sitepoint.com/wp-content/uploads/2021/10/1633515109react-props-2.png",
"slug": "blog/third-post",
"excerpt": "Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
"body": "Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
}
]
JSON 数组包含我们将在博客页面上呈现的博客文章。通常此类数据应从 API 获取,而不是存储在 JSON 对象中。
接下来,在标记中导入并使用它,如下所示:
// next-portfolio/pages/blog/index.js
import Posts from '../posts.json'
import Link from 'next/link'
export default function Blogs() {
return (
<div className="container">
<h1> Latest Posts </h1>
<div className="posts">
{Posts.map(post => {
return(
<div className="post" key={post.id}>
<img src={post.cover} />
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
<Link href={post.slug}>
<a>Read Post</a>
</Link>
</div>
)}
)}
</div>
</div>
)
}
为了使页面看起来更好,这里有一些样式:
// next-portfolio/styles/globals.css
/* BLOG PAGE */
.posts {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 50px;
max-width: 1200px;
margin: 0 auto;
}
.post-container {
margin: 15px auto;
max-width: 900px;
}
.post-container img {
width: 100%;
}
.post img {
height: 300px;
width: 500px;
}
.posts a {
background-color: black;
color: #D7E5f0;
padding: 10px 10px;
cursor: pointer;
margin: 30px 0;
border-radius: 6px;
}
.post {
background-color: white;
margin: 30px 0;
padding: 30px 30px;
border-radius: 6px;
}
现在请在您的网络浏览器上导航到http://localhost:3000/blog 。
在本节中,我们将做两件事:
为此,请进入pages/blog并创建一个名为[blog]. 在文件夹中,创建两个文件,[index].js 和 [comments].js
my-site
└── pages
├── index.js // index route (will be rendered at my-site.com)
└── blog
├── index.js // list of blog post (my-site.com/blog)
└── [blog]
├── [index].js // (eg: my-site.com/blog/first-post)
├── [comments].js // (eg: my-site.com/blog/first-post/comments)
导航到 [index].js 并输入以下代码:
import {useRouter} from 'next/router'
import Link from 'next/link'
import Posts from '../../posts.json'
export default function Blog() {
const router = useRouter();
const {blog} = router.query;
const fullPath = blog+"/comments";
if (blog === "first-post") {
return (
<div className="post-container">
<div>
<img src={Posts[0].cover}
<h1> {Posts[0].title}</h1>
<p>{Posts[0].body}</p>
<p>{Posts[0].body}</p>
<p>{Posts[0].body}</p>
<hr />
<div className="comments">
<h3>Comments</h3>
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<Link href={fullPath}>
<a>Read all comments for this article</a>
</Link>
</div>
</div>
</div>
)
} else if (blog === "second-post") {
return (
<div className="post-container">
<div>
<img src={Posts[1].cover} <h1> {Posts[1].title}</h1>
<p>{Posts[1].body}</p>
<p>{Posts[1].body}</p>
<p>{Posts[1].body}</p>
<hr />
<div className="comments">
<h3>Comments</h3>
<p>Marina Costa</p>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<Link href={fullPath}>
<a>Read all comments for this article</a>
</Link>
</div>
</div>
</div>
)
} else {
return (
<div className="post-container">
<div>
<img src={Posts[2].cover}
<h1> {Posts[2].title}</h1>
<p>{Posts[2].body}</p>
<p>{Posts[2].body}</p>
<p>{Posts[2].body}</p>
<hr />
<div className="comments">
<h3>Comments</h3>
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<Link href={fullPath}>
<a>Read all comments for this article</a>
</Link>
</div>
</div>
</div>
)}
}
请注意,在实际项目中,您不需要if条件语句来基于post :id. 那是因为您通常会将所有帖子存储在数据库中。然后,您只需在 API 中查询与查询 ID 匹配的帖子。
代码看起来类似于:
import Link from 'next/link'
export default function Blog( {post} ) {
return (
<div className="post-container">
<div>
<img src={posts.cover}
<h1> {post.title}</h1>
<p>{post.body}</p>
<hr />
<div className="comments">
<h3>Comments</h3>
<h5>{post.commenter}</h5>
<p>{post.featured_comment}</p>
<Link href={post.fullPath}>
<a>Read all comments for this article</a>
</Link>
</div>
</div>
</div>
)}
}
export async const getStaticProps = ({ params }) => {
const res = await fetch(`https://your-api.com/posts/${params.title}`);
const post = await res.json();
return {
props: { post },
};
}
观察我们如何消除对useRouter(). 这是因为getStaticProps()自动从对象中获取查询 ID,该param对象是上下文对象的一部分。然后从 API 中检索与该标题匹配的帖子对象,并将其传递props给Blog组件。
现在我们已经建立了获取外部数据的正确方法,是时候查看单个帖子页面的样子了:http://localhost:3000/blog/first-post。
你还记得[comments].js我们之前创建的文件吗?Next.js 会将此页面视为嵌套页面:
//next-portfolio/pages/blog/[blog]/[comments].js
import {useRouter} from 'next/router'
export default function Comments() {
const router = useRouter();
const {blog} = router.query;
return (
<div className="container">
<div>
<h2> You are now reading the comments from the {blog} </h2>
<div className="comments">
<h3>Comments</h3>
<hr />
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<hr />
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<hr />
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<hr />
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<hr />
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
<hr />
<h5>Marina Costa</h5>
<p>Absolutely spot on! Thanks for sharing, Kingsley!</p>
</div>
</div>
</div>
)
}
这是您在现实项目中通常会做的事情:
export default function Comments( {comments} ) {
return (
<div className="container">
<div>
<h2> You are now reading the comments from the {blog} </h2>
<div className="comments">
{comments.map(comment => {
return(
<div className="comment" key={comment.id}>
<h5>{comment.name}</h5>
<p>{comment.body}</p>
<hr />
</div>
)}
)}
</div>
</div>
</div>
)
}
export async const getStaticProps = ({ params }) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/blog-comments/${params.title}`);
const comments = await res.json();
return {
props: { comments },
};
}
Next.js 中的页面路由是 Next 中最重要的概念之一。它也是最强大的功能,因为您可以根据需要构建您的网站,并通过嵌套路由在路由之间传递数据。
在本教程中,我们通过构建一个简单的投资组合网站了解了很多关于 Next.js 中页面路由的实现。