在Next.js中使用Link链接两个页面的文章中,我们看到了如何将主页链接到博客页面。
博客是Next.js的一个很好的用例,我们将在本章中通过添加博客文章继续探索。
博客文章有一个动态的URL。例如,一篇题为 "Hello World "的文章的URL是/blog/hello-world 。一篇题为 "我的第二篇文章 "的文章可能有一个URL/blog/my-second-post 。
这些内容是动态的,可能来自数据库、markdown文件或更多。
Next.js可以根据动态URL来提供动态内容。
我们通过使用[] 语法创建一个动态页面来创建一个动态URL。
如何创建?我们添加一个pages/blog/[id].js 文件。这个文件将处理/blog/ 路线下的所有动态URL,比如我们上面提到的那些。/blog/hello-world,/blog/my-second-post 等等。
在文件名中,方括号内的[id] 意味着任何动态的东西都将被放在路由器的查询属性的id 参数内。
好吧,这一下子有点多了。
路由器是什么?
路由器是Next.js提供的一个库。
我们从next/router 中导入它。
import { useRouter } from "next/router";
而一旦我们有了useRouter ,我们就使用实例化的路由器对象。
const router = useRouter();
一旦我们有了这个路由器对象,我们就可以从中提取信息。
特别是我们可以通过访问router.query.id ,获得[id].js 文件中URL的动态部分。
因此,让我们继续在实践中应用所有这些东西。
创建文件pages/blog/[id].js 。
import { useRouter } from "next/router";
export default () => {
const router = useRouter();
return (
<>
<h1>Blog post</h1>
<p>Post id: {router.query.id}</p>
</>
);
};
现在,如果你进入http://localhost:3000/blog/test 路由器,你应该看到这个。

我们可以使用这个id 参数,从一个帖子的列表中收集帖子。例如,从一个数据库中。为了保持简单,我们将在项目根文件夹中添加一个posts.json 文件。
{
"test": {
"title": "test post",
"content": "Hey some post content"
},
"second": {
"title": "second post",
"content": "Hey this is the second post content"
}
}
现在我们可以导入它并从id 关键中查找帖子。
import { useRouter } from "next/router";
import posts from "../../posts.json";
export default () => {
const router = useRouter();
const post = posts[router.query.id];
return (
<>
<h1>{post.title}</h1>
<p>{post.content}</p>
</>
);
};
重新加载页面应该向我们显示这个结果。

但事实并非如此!相反,我们在控制台中得到一个错误,在浏览器中也有一个错误。

为什么?因为......在渲染过程中,当组件被初始化时,数据还不在那里。我们将在下一课看到如何用getInitialProps向组件提供数据。
现在,在返回JSX之前添加一个小小的if (!post) return <p></p> 检查。
import { useRouter } from "next/router";
import posts from "../../posts.json";
export default () => {
const router = useRouter();
const post = posts[router.query.id];
if (!post) return <p></p>;
return (
<>
<h1>{post.title}</h1>
<p>{post.content}</p>
</>
);
};
现在事情应该可以了。最初,该组件在没有动态router.query.id 信息的情况下被渲染。渲染后,Next.js触发了查询值的更新,页面显示了正确的信息。
而如果你查看源代码,HTML中就有那个空的<p> 标签。

我们很快就会修复这个未能实现SSR的问题,这既损害了用户的加载时间,也损害了我们已经讨论过的SEO和社会分享。
我们可以通过在pages/blog.js 中列出这些帖子来完成这个博客例子。
import posts from "../posts.json";
const Blog = () => (
<div>
<h1>Blog</h1>
<ul>
{Object.entries(posts).map((value, index) => {
return <li key={index}>{value[1].title}</li>;
})}
</ul>
</div>
);
export default Blog;
我们可以通过从next/link 中导入Link ,并在帖子循环中使用它,将它们链接到各个帖子页面。
import Link from "next/link";
import posts from "../posts.json";
const Blog = () => (
<div>
<h1>Blog</h1>
<ul>
{Object.entries(posts).map((value, index) => {
return (
<li key={index}>
<Link href="/blog/[id]" as={"/blog/" + value[0]}>
<a>{value[1].title}</a>
</Link>
</li>
);
})}
</ul>
</div>
);
export default Blog;