路由
Next.js 使用文件系统路由。包括:App Router 和 Pages Router
虽然可以在同一个项目中使用两种路由,但 app 中的路由将优先于 pages。建议在新项目中只使用一种路由,以避免混淆。官方推荐使用 App Router
Pages Router
1、创建一个 pages
文件夹,在 pages 文件夹中添加一个 index.tsx
文件,这就是首页 /
:
export default function Page() {
return <h1>Hello, Next.js!</h1>;
}
2、在 pages 中添加一个 _app.tsx
文件用来定义全局布局:
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
3、在 pages 中添加一个 _document.tsx
文件来控制来自服务器的初始响应:
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
App Router
目录解析
src/
└── app
├── page.tsx
├── layout.tsx
├── template.tsx
├── loading.tsx
├── error.tsx
├── not-found.tsx
└── dashboard
└── components
└── page.tsx
└── settings
└── page.tsx
app/page.tsx
对应路由/
app/dashboard/page.tsx
对应路由/dashboard
app/dashboard/settings/page.js
对应路由/dashboard/settings
app/dashboard/components
目录下因为没有 page.tsx 文件,所以没有对应的路由。这种文件可以用来存放组件、样式、图片或者其他文件。
注意:.js
、.jsx
、.tsx
文件都是支持的
特殊文件
Next.js 约定了一些特殊文件,这些文件可以是.js
、.jsx
、.tsx
文件 | 描述 |
---|---|
layout | 共享的布局 |
page | 页面,可公开访问 |
loading | 加载状态 |
error | 错误处理 |
not-found | 404 页面 |
示例
1、创建一个 app
文件夹,然后添加 layout.tsx
和 page.tsx
文件。当用户访问应用程序的根目录 /
时,将呈现这些内容。
2、在 layout.tsx 中创建一个带有所需 html 和 body 标签的根布局:
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
3、创建一个包含一些初始内容的首页:
export default function Page() {
return <h1>Hello, Next.js!</h1>;
}
根布局
使用 create-next-app 默认创建的 layout.js 代码如下:
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
- app 目录必须包含根布局
- 根布局必须包含 html 和 body 标签,其他布局不能包含这些标签
- 可以通过 Metadata API 配置元数据
- 根布局默认是服务端组件,且不能设置为客户端组件
- 可以使用路由组创建多个根布局
路由配置
Next.js 使用基于文件系统的路由器,其中文件夹用于定义路由。
嵌套路由:将文件夹相互嵌套。如创建 app/users/new/page.tsx
,然后可以通过 /users/new
访问。
注意:必须要有 page.tsx
文件,否则无法公开访问。page 的后缀可以是 .js
、.jsx
、.tsx
。
路由导航
路由导航有 4 种方式:
- 使用 Link 组件
- useRouter (客户端组件)
- redirect (服务端组件)
- 浏览器的 History API
1、使用 Link 组件
如果使用 a 标签,会重新加载整个页面。使用 Link 组件,会局部刷新。
在生产环境中,每当 Link 组件出现在浏览器的视口中时,Next.js 会自动在后台预取链接路由的代码。当用户点击链接时,目标页面的代码已经在后台加载。
import Link from 'next/link';
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>;
}
可以使用 usePathname()
来获取当前路径,但是不能在服务端使用。
'use client';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
export function Links() {
const pathname = usePathname();
return (
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
);
}
2、useRouter (客户端组件)
useRouter 以编程方式在客户端组件内的路由之间进行导航。
'use client';
import { useRouter } from 'next/navigation';
export default function Page() {
const router = useRouter();
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
);
}
路由参数
useSearchParams
访问当前 URL 的参数。如 /dashboard/invoices?page=1&query=pending
的结果是:{ page: '1', query: 'pending' }
usePathname
读取当前 URL 的路径名。例如,对于路由/dashboard/invoices
,将返回 '/dashboard/invoices'
'use client';
import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
export default function NavigationEvents() {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
const url = `${pathname}?${searchParams}`;
console.log(url);
}, [pathname, searchParams]);
return null;
}
动态路由
[folderName]
使用动态路由,需要将文件夹的名字用方括号括住,比如 [id]
、[slug]
。这个路由的名字会传递给 params
对象。
创建 app/users/[id]/page.tsx
:
export default function Page({ params }: { params: { id: string } }) {
return <div>userID: {params.id}</div>;
}
在 app/users/page.tsx
中跳转,并传递参数:
<Link href="/users/1">user1</Link>
<Link href="/users/2">user2</Link>