如何在 Next.js 網站中設置動態內容?

使用 Link 在 Next.js 中連接兩個頁面文章中,我們看到了如何將主頁連接到博客頁面。

博客是 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參數中。

好的,一次介紹了這麼多東西。

什麼是路由器(router)

路由器是 Next.js 提供的一個庫。

我們從 next/router 導入它:

import { useRouter } from "next/router";

一旦我們有了 useRouter,我們可以使用以下代碼來實例化路由器對象:

const router = useRouter();

一旦我們有了這個路由器對象,我們可以從中提取信息。

特別是我們可以通過訪問 [id].js 文件中的 router.query.id 來獲取 URL 的動態部分。

讓我們繼續實踐這些事情。

創建文件 pages/blog/[id].js

import { useRouter } from "next/router";

export default () => {
  const router = useRouter();

  return (
    <>
      <h1>博客文章</h1>
      <p>文章 ID: {router.query.id}</p>
    </>
  );
};

現在,如果您轉到 http://localhost:3000/blog/test ,您應該會看到以下內容:

我們可以使用此 id 參數從一個文章列表中獲取文章。例如,可以從數據庫獲取文章。為了保持簡單,我們將在項目根目錄中添加一個 posts.json 文件:

{
  "test": {
    "title": "測試文章",
    "content": "嘿,這是一些文章內容"
  },
  "second": {
    "title": "第二篇文章",
    "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>博客</h1>

    <ul>
      {Object.entries(posts).map((value, index) => (
        <li key={index}>{value[1].title}</li>
      ))}
    </ul>
  </div>
);

export default Blog;

我們可以通過導入 next/link 並在文章循環中使用它將它們鏈接到個別的博客文章頁面:

import Link from "next/link";
import posts from "../posts.json";

const Blog = () => (
  <div>
    <h1>博客</h1>

    <ul>
      {Object.entries(posts).map((value, index) => (
        <li key={index}>
          <Link href="/blog/[id]" as={`/blog/${value[0]}`}>
            <a>{value[1].title}</a>
          </Link>
        </li>
      ))}
    </ul>
  </div>
);

export default Blog;