Next.js手把手系列:02、路由篇 | App Router

  • Louis
  • 更新于 2024-07-14 10:21
  • 阅读 1038

路由(Router)是Next.js应用的重要组成部分。在Next.js中,路由决定了一个页面如何渲染或者一个请求该如何返回。Next.js有两套路由解决方案,之前的方案称之为“PagesRouter”,目前的方案称之为“AppRouter”,两套方案目前是兼容的。

前言

路由(Router)是 Next.js 应用的重要组成部分。在 Next.js 中,路由决定了一个页面如何渲染或者一个请求该如何返回。

Next.js 有两套路由解决方案,之前的方案称之为“Pages Router”,目前的方案称之为“App Router”,两套方案目前是兼容的,都可以在 Next.js 中使用。

从 v13.4 起,App Router 已成为默认的路由方案,新的 Next.js 项目建议使用 App Router。

本篇我们会学习 App Router 下路由的定义方式和常见的文件约定。

1. 文件系统(file-system)

Next.js 的路由基于的是文件系统,也就是说,一个文件就可以是一个路由。举个例子,你在 pages 目录下创建一个 index.js 文件,它会直接映射到 / 路由地址:

// pages/index.js
import React from 'react'
export default () => <h1>Hello world</h1>

在 pages 目录下创建一个 about.js 文件,它会直接映射到 /about 路由地址:

// pages/about.js
import React from 'react'
export default () => <h1>About us</h1>

2. 从 Pages Router 到 App Router

现在你打开使用 create-next-app 创建的项目,你会发现默认并没有 pages 这个目录。查看 packages.json中的 Next.js 版本,如果版本号大于 13.4,那就对了!

Next.js 从 v13 起就使用了新的路由模式 —— App Router。之前的路由模式我们称之为“Pages Router”,为保持渐进式更新,依然存在。从 v13.4 起,App Router 正式进入稳定化阶段,App Router 功能更强、性能更好、代码组织更灵活,以后就让我们使用新的路由模式吧!

可是这俩到底有啥区别呢?Next.js 又为什么升级到 App Router 呢?知其然知其所以然,让我们简单追溯一下。以前我们声明一个路由,只用在 pages 目录下创建一个文件就可以了,以前的目录结构类似于:

└── pages
    ├── index.js
    ├── about.js
    └── more.js

这种方式有一个弊端,那就是 pages 目录的所有 js 文件都会被当成路由文件,这就导致比如组件不能写在 pages 目录下,这就不符合开发者的使用习惯。(当然 Pages Router 还有很多其他的问题,只不过目前我们介绍的内容还太少,为了不增加大家的理解成本,就不多说了)

升级为新的 App Router 后,现在的目录结构类似于:

src/
└── app
    ├── page.js 
    ├── layout.js
    ├── template.js
    ├── loading.js
    ├── error.js
    └── not-found.js
    ├── about
    │   └── page.js
    └── more
        └── page.js

使用新的模式后,你会发现 app 下多了很多文件。这些文件的名字并不是我乱起的,而是 Next.js 约定的一些特殊文件。从这些文件的名称中你也可以了解文件实现的功能,比如布局(layout.js)、模板(template.js)、加载状态(loading.js)、错误处理(error.js)、404(not-found.js)等。

简单的来说,App Router 制定了更加完善的规范,使代码更好被组织和管理。至于这些文件具体的功能和介绍,不要着急,本篇我们会慢慢展开。

3. 使用 Pages Router

当然你也可以继续使用 Pages Router,如果你想使用 Pages Router,只需要在 src 目录下创建一个 pages 文件夹或者在根目录下创建一个 pages文件夹。其中的 JS 文件会被视为 Pages Router 进行处理。

但是要注意,虽然两者可以共存,但 App Router 的优先级要高于 Pages Router。而且如果两者解析为同一个 URL,会导致构建错误。

Xnip2024-07-14_09-27-19.png

注意:你在 Next.js 官方文档进行搜索的时候,左上角会有 App 和 Pages 选项,这对应的就是 App Router 和 Pages Router

Xnip2024-07-14_09-28-05.png

因为两种路由模式的使用方式有很大不同,所以搜索的时候注意选择正确的的路由模式。

4. 使用 App Router

4.1. 定义路由(Routes)

现在让我们开始正式的学习 App Router 吧。

首先是定义路由,文件夹被用来定义路由。每个文件夹都代表一个对应到 URL 片段的路由片段。创建嵌套的路由,只需要创建嵌套的文件夹。举个例子,下图的 app/dashboard/settings目录对应的路由地址就是 /dashboard/settings

Xnip2024-07-14_09-30-12.png

4.2. 定义页面(Pages)

那如何保证这个路由可以被访问呢?你需要创建一个特殊的名为 page.js 的文件。至于为什么叫 page.js呢?除了 page 有“页面”这个含义之外,你可以理解为这是一种约定或者规范。(如果你是 Next.js 的开发者,你也可以约定为 index.js甚至 louis.js!)

Xnip2024-07-14_09-31-20.png

在上图这个例子中:

  • app/page.js 对应路由 /
  • app/dashboard/page.js 对应路由 /dashboard
  • app/dashboard/settings/page.js 对应路由/dashboard/settings
  • analytics 目录下因为没有 page.js 文件,所以没有对应的路由。这个文件可以被用于存放组件、样式表、图片或者其他文件。

当然不止 .js文件,Next.js 默认是支持 React、TypeScript 的,所以 .js.jsx.tsx 都是可以的。

那 page.js 的代码该如何写呢?最常见的是展示 UI,比如:

// app/page.js
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

访问 http://localhost:3000/,效果如下:

Xnip2024-07-14_09-33-23.png

4.3. 定义布局(Layouts)

布局是指多个页面共享的 UI。在导航的时候,布局会保留状态、保持可交互性并且不会重新渲染,比如用来实现后台管理系统的侧边导航栏。

定义一个布局,你需要新建一个名为 layout.js的文件,该文件默认导出一个 React 组件,该组件应接收一个 children prop,chidren 表示子布局(如果有的话)或者子页面。

举个例子,我们新建目录和文件如下图所示:

Xnip2024-07-14_09-35-07.png

相关代码如下:

// app/dashboard/layout.js
export default function DashboardLayout({
  children,
}) {
  return (
    <section>
      <nav>nav</nav>
      {children}
    </section>
  )
}
// app/dashboard/page.js
export default function Page() {
  return <h1>Hello, Dashboard!</h1>
}

当访问 /dashboard的时候,效果如下:

Xnip2024-07-14_09-36-40.png

其中,nav 来自于 app/dashboard/layout.jsHello, Dashboard! 来自于 app/dashboard/page.js

你可以发现:同一文件夹下如果有 layout.js 和 page.js,page 会作为 children 参数传入 layout。换句话说,layout 会包裹同层级的 page。

app/dashboard/settings/page.js 代码如下:

// app/dashboard/settings/page.js 
export default function Page() { 
    return \<h1>Hello, Settings!\</h1> 
}

当访问 /dashboard/settings的时候,效果如下

Xnip2024-07-14_09-38-36.png

其中,nav 来自于 app/dashboard/layout.jsHello, Settings! 来自于 app/dashboard/settings/page.js

你可以发现:布局是支持嵌套的app/dashboard/settings/page.js 会使用 app/layout.js 和 app/dashboard/layout.js 两个布局中的内容,不过因为我们没有在 app/layout.js 写入可以展示的内容,所以图中没有体现出来。

根布局(Root Layout)

布局支持嵌套,最顶层的布局我们称之为根布局(Root Layout),也就是 app/layout.js。它会应用于所有的路由。除此之外,这个布局还有点特殊。

使用 create-next-app 默认创建的 layout.js 代码如下:

// app/layout.js
import './globals.css'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  )
}

其中:

  1. app 目录必须包含根布局,也就是 app/layout.js 这个文件是必需的。
  2. 根布局必须包含 html 和 body标签,其他布局不能包含这些标签。如果你要更改这些标签,不推荐直接修改。
  3. 你可以使用路由组创建多个根布局。
  4. 默认根布局是服务端组件且不能设置为客户端组件。

4.4. 定义模板(Templates)

模板类似于布局,它也会传入每个子布局或者页面。但不会像布局那样维持状态。

模板在路由切换时会为每一个 children 创建一个实例。这就意味着当用户在共享一个模板的路由间跳转的时候,将会重新挂载组件实例,重新创建 DOM 元素,不保留状态。这听起来有点抽象,没有关系,我们先看看模板的写法,再写个 demo 你就明白了。

定义一个模板,你需要新建一个名为 template.js 的文件,该文件默认导出一个 React 组件,该组件接收一个 children prop。我们写个示例代码。

在 app目录下新建一个 template.js文件:

Xnip2024-07-14_09-41-36.png

template.js 代码如下:


// app/template.js
export default function Template({ children }) {
  return <div>{childr...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Louis
Louis
web3 developer,技术交流或者有工作机会可加VX: magicalLouis