Learn Next.js 官方教學筆記 - Chapter 5 : Navigating Between Pages
此系列為 Next.js 官方教學 Learn Next.js 的筆記。
文章連結:Chapter 5 : Navigating Between Pages。
在前一個章節,我們建立了 dashboard 的 layout 以及 pages。
現在要來新增一些連結讓使用者可以在 dashboard routes 間作切換。
在這一章節可以學到以下幾個項目:
- 怎麼使用 next/link 元件。
- 怎麼使用
usePathname()
hook 來顯示 active link。 - navigation 怎麼在 Next.js 運作的。
Why optimize navigation?
為了使各個頁面有連結,傳統上會使用 <a>
HTML tag 。且在現在的程式中,sidebar link 就是使用 <a>
。但需要注意一些事情,就是你有發現當你點擊這些連結時發生了什麼嗎?
沒錯!這樣的方式會造成整個頁面的重新渲染。
The <Link>
component
在 Next.js 中,你可以使用 <Link />
component 來連結應用程式間的各個頁面。<Link />
可以讓你用 JavaScript 來使用 client-side navigation。
所以為了使用 <Link />
,打開 /app/ui/dashboard/nav-links.tsx,然後從 next/link
import Link
元件。最後用 <Link />
取代 <a>
:
import {
UserGroupIcon,
HomeIcon,
DocumentDuplicateIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
// ...
export default function NavLinks() {
return (
<>
{links.map((link) => {
const LinkIcon = link.icon;
return (
<Link
key={link.name}
href={link.href}
className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
>
<LinkIcon className="w-6" />
<p className="hidden md:block">{link.name}</p>
</Link>
);
})}
</>
);
}
就如同你所看到的,Link
元件的用法跟 <a>
tag 用法很相似,但不是使用 <a href="…">
而是使用 <Link href="…">
。
所以在更改並存儲後,可以看到當點擊切換頁面的按鈕後,會發現頁面並不會全部重新整理。
Automatic code-splitting and prefetching
為了改善 navigation 的體驗,Next.js 會自動利用 route segments 切分程式碼。這樣的方式跟傳統的 React SPA 在最初載入時就會載入全部的程式進瀏覽器的方式不太一樣。
利用 routes 來切分程式碼代表頁面變成獨立的。所以若頁面發生錯誤了,剩下的程式還是可以照常執行。
更甚者,不論 <Link>
元件何時會出現在瀏覽器的 viewpoint 中,Next.js 都會自動為了linked route,在背景中 prefetch 程式碼。而在使用者點擊 link 前,這個頁面的程式就會先在背景中被載入,藉此來讓頁面間的轉換看起來像是及時的。
更多內容可以參考 how navigation works。
Pattern: Showing active links
一個常見的 UI pattern 是藉由顯示 active link 來指出使用者當前在哪個頁面。為了做到這點,你需要從 URL 去得到使用者當前的路徑。Next.js 提供一個 hook usePathname()
來讓你去確認並執行這個 pattern。
而因為 usePathname()
是一個 hook ,所以你需要把 nav-links.tsx
轉換為一個 Client Component。並加上 React 的 "use client"
在檔案的最上方,然後從 next/navigation
import usePathname()
:
'use client';
import {
UserGroupIcon,
HomeIcon,
InboxIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
// ...
接下來在 <NavLinks />
裡利用 usePathname()
賦值給一個叫 pathname
的變數:
export default function NavLinks() {
const pathname = usePathname();
// ...
}
接下來可以使用 clsx
這個 library (在 CSS styling 介紹過)來讓 link active時,可以依據條件來應用不同的 className。條件是當 link.href
與 pathname
符合時,link 會顯示藍色的字以及藍色的背景。
以下是最終的程式碼:
'use client';
import {
UserGroupIcon,
HomeIcon,
DocumentDuplicateIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import clsx from 'clsx';
// ...
export default function NavLinks() {
const pathname = usePathname();
return (
<>
{links.map((link) => {
const LinkIcon = link.icon;
return (
<Link
key={link.name}
href={link.href}
className={clsx(
'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
{
'bg-sky-100 text-blue-600': pathname === link.href,
},
)}
>
<LinkIcon className="w-6" />
<p className="hidden md:block">{link.name}</p>
</Link>
);
})}
</>
);
}