連結與導航
Next.js 的路由器允許您在頁面之間進行客戶端路由轉換,類似於單頁應用程式。
提供了一個名為 Link
的 React 元件來實現這種客戶端路由轉換。
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">首頁</Link>
</li>
<li>
<Link href="/about">關於我們</Link>
</li>
<li>
<Link href="/blog/hello-world">部落格文章</Link>
</li>
</ul>
)
}
export default Home
上面的範例使用了多個連結。每個連結都將路徑 (href
) 映射到已知頁面:
/
→pages/index.js
/about
→pages/about.js
/blog/hello-world
→pages/blog/[slug].js
視窗內任何 <Link />
(初始或通過滾動)都會預設預取使用 靜態生成 (Static Generation) 的頁面(包括對應的資料)。對於 伺服器渲染 (server-rendered) 的路由,對應的資料僅在點擊 <Link />
時才會獲取。
連結到動態路徑
您也可以使用插值來創建路徑,這對於 動態路由區段 (dynamic route segments) 非常有用。例如,顯示作為 prop 傳遞給元件的文章列表:
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
{post.title}
</Link>
</li>
))}
</ul>
)
}
export default Posts
範例中使用
encodeURIComponent
來保持路徑的 utf-8 相容性。
或者,使用 URL 物件:
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: post.slug },
}}
>
{post.title}
</Link>
</li>
))}
</ul>
)
}
export default Posts
現在,我們在 href
中使用 URL 物件而不是插值來創建路徑,其中:
pathname
是pages
目錄中的頁面名稱。本例中為/blog/[slug]
。query
是一個包含動態區段的物件。本例中為slug
。
注入路由器
要在 React 元件中存取 router
物件,您可以使用 useRouter
或 withRouter
。
一般我們推薦使用 useRouter
。
命令式路由
next/link
應該能滿足大多數路由需求,但您也可以在不使用它的情況下進行客戶端導航,請參閱 next/router
文件。
以下範例展示如何使用 useRouter
進行基本頁面導航:
import { useRouter } from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
點擊此處閱讀更多
</button>
)
}
淺層路由
淺層路由允許您更改 URL 而不再次執行資料獲取方法,這包括 getServerSideProps
、getStaticProps
和 getInitialProps
。
您將通過 router
物件(由 useRouter
或 withRouter
添加)接收更新的 pathname
和 query
,而不會丟失狀態。
要啟用淺層路由,請將 shallow
選項設為 true
。考慮以下範例:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// 當前 URL 是 '/'
function Page() {
const router = useRouter()
useEffect(() => {
// 總是在首次渲染後進行導航
router.push('/?counter=10', undefined, { shallow: true })
}, [])
useEffect(() => {
// 計數器改變了!
}, [router.query.counter])
}
export default Page
URL 將更新為 /?counter=10
且頁面不會被替換,僅路由狀態會改變。
您也可以通過 componentDidUpdate
監聽 URL 變更,如下所示:
componentDidUpdate(prevProps) {
const { pathname, query } = this.props.router
// 驗證 props 是否已變更以避免無限循環
if (query.counter !== prevProps.router.query.counter) {
// 根據新查詢獲取資料
}
}
注意事項
淺層路由僅適用於當前頁面的 URL 變更。例如,假設我們有另一個名為 pages/about.js
的頁面,您執行以下操作:
router.push('/?counter=10', '/about?counter=10', { shallow: true })
由於這是一個新頁面,即使我們要求進行淺層路由,它也會卸載當前頁面,載入新頁面並等待資料獲取。
當淺層路由與中介軟體 (middleware) 一起使用時,它不會像以前沒有中介軟體時那樣確保新頁面與當前頁面匹配。這是由於中介軟體能夠動態重寫,並且在沒有資料獲取的情況下無法在客戶端驗證(淺層路由會跳過資料獲取),因此淺層路由變更必須始終被視為淺層。