路由系统
在进行正式的auth开发之前,让我们先来学习一下Next.js框架中的路由是如何工作的。如果你对这块已经非常熟悉了,可以跳过这章,直接进入下一章。如果不熟悉,可以通过这章内容来实操了解一下,也可以直接访问Next.js官方文档 来查看详细的讲解和说明。我们这个项目是Next.js 14.2.24,并且是app router
,大家可以在左上角选择对应的版本查看相应的文档。
子路由
为什么我们最开始查看Button
组件效果的时候直接在app
目录下的page.tsx
文件中修改就可以在 http://localhost:3000 这个路由地址中看到相应的内容呢?因为Next.js框架的路由机制就是这样设计的:
- app目录下的
page.tsx
就默认是根路由页面,也就是访问 http://localhost:3000 可以看到的页面。 - app目录下的
layout.tsx
就默认是根布局(root layout),根布局中的设置作用于整个项目的所有页面。
那么,如果我们想要创建一些子路由,比如我们项目可能会有对登录用户可见的dashboard
页面,那应该怎么设置呢?很简单,只需要在app目录下创建一个dashboard
目录即可,然后在dashboard目录下创建一个page.tsx
,并写入页面内容,那么我们就可以通过 http://localhost:3000/dashboard 来访问这个Dashboard页面了。
让我们来实操一下:
export default function DashboardPage() {
return (
<div>
<h1>This is dashboard page.</h1>
</div>
);
}
现在访问 http://localhost:3000/dashboard 就可以看到:
在路由页面中,我们一定要使用export default function
这种默认导出的形式,否则next.js将无法识别这个页面。
另外还需要注意,如果你没有在你创建的子路由目录下添加page.tsx
文件,而直接访问 http://localhost:3000/dashboard ,那么你会看到一个404页面:
所以,如果我们在以后的开发工作中遇到404页面,要么我们没有在该路由下创建相应的页面,要么就是路由地址出现错误,一定要仔细检查路由地址,尤其是在复杂项目中的多层嵌套路由和动态路由混合使用的情况下,很容易出现类似的错误。
嵌套路由
接下来,我们就来实操一个嵌套路由。在dashboard
目录下再创建一个settings
目录,然后在里面新建page.tsx
:
export default function SettingsPage() {
return (
<div>
<h1>This is settings page of dashboard.</h1>
</div>
);
}
现在访问 http://localhost:3000/dashboard/settings 就可以看到:
这样,我们就完成了一个嵌套路由。在以后的开发中,如果我们想在一个模块下创建子页面,就可以使用这种嵌套路由的形式。理论上,可以添加无限层嵌套和无限多的页面,但实际开发中,我们当然还需要考虑浏览器地址栏中url的长度,以及一些页面的必要性。
这时候,有人可能会好奇了,比如一个博客网站,每篇文章都有一个url地址,难道我们要在项目中创建无数个路由文件吗?当然不需要。因为Next.js中还有一个动态路由来解决这个问题。由于我们这个项目中不会涉及到动态路由的使用,所以如果大家感兴趣的话,可以去查看Next.js官方文档中关于Dynamic Routes 的讲解。
布局
讲完路由,让我们来了解一下Next.js中的布局机制。前面我们已经介绍过,app
目录下的layout.tsx
默认是根布局(root layout),根布局中的设置作用于整个项目的所有页面。那么同理,如果我们在dashboard
目录下也创建一个layout.tsx
,它当然也会作用于dashboard下面的所有子页面。
现在,让我们来观察一下RootLayout文件中的代码结构:
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
前19行都是关于样式、字体和页面元信息的,我们先不看,直接看RootLayout
函数中的内容,这个函数接收一个ReactNode
类型的children
参数,然后在下面的return
中,在html
标签中的body
里面渲染出来。这段代码的意思就是,在这个layout文件作用范围内的所有页面,都会被作为children
参数传递到这个html
标签中的body
里面被渲染出来。
那么,我们现在来创建我们的dashboard layout。由于RootLayout中已经有了html
和body
标签,而且这两个标签也确实就应该在RootLayout中进行设置,所以我们当然就不需要再在dashboard layout中添加这两个标签了。
一般项目中的dashboard页面会有一个最上面的导航栏(navbar),访问dashboard所有子页面时都应该可以看到这个navbar,所以我们就在dashboard layout中创建一个nav
标签,然后看看它会不会在dashboard及其子页面settings中显示出来:
export default function DashboardLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<>
<nav className="bg-purple-300">
Dashboard Navbar
</nav>
<div>
{children}
</div>
</>
);
}
现在访问 http://localhost:3000/dashboard 就可以看到:
然后访问 http://localhost:3000/dashboard/settings 也可以看到:
那么我们的navbar在dashboard页面和它的子页面settings里面都正常显示了。如果你还不放心,可以在dashboard目录下添加users目录,再把settings目录下的page.tsx
复制过去,修改一下,再访问,也可以看到这个navbar。我这里就不再做演示了。所以子路由目录下的layout.tsx
文件的功能就是创建一些在这个子路由下需要共享的布局样式,比如导航栏、侧边栏、footer等等。
由于app目录下的layout.tsx是负责整个项目的html结构的渲染的,所以这个文件以及里面return部分的html结构不能删除,你可以删除或替换其中的字体设置,以及在以后的开发中可能会在在{children}
外面包裹一些作用于全局的providers
,但是整体的结构是不能动的,否则页面就无法正常渲染了。当然像dashboard这些子目录下面的layout文件还是可以删除的,只要你不再需要了就行。
隐藏路由
我们这个项目是关于用户鉴权(auth)的,那么我们就可以在app目录下创建一个auth目录,然后再在auth目录下面添加sign-up目录和sign-in目录用来设置注册页面和登录页面。这样的话,我们的注册和登录页面地址就分别是:http://localhost:3000/auth/sign-up 和 http://localhost:3000/auth/sign-in 。
但是其实我们在实践中一般看到的登录和注册页面的路由地址都是https://www.mydomain.com/sign-in
这样的,在根路由下面并没有一个auth子路由。
假如直接把跟auth相关的路由目录直接放在app目录下,又会显得多而杂乱,因为后续还会有密码重置路由、验证链接路由等,而且项目的其他模块也有可能需要一些一级子路由页面,如果就这样散乱地放在app目录下,会显得非常没有章法,所以把同样功能模块的路由放在一个统一的目录下是一个很好的开发实践。
那么怎么在保留auth目录的情况下又隐藏掉url中的auth呢?
Next.js的路由机制给我们提供了一个方便的办法,那就是在auth
目录名上加个()
,即将这个文件夹命名为(auth)
,它就不用出现在url地址中了(注意要用英文小括号)。
让我们实际操作一下,在app目录下创建(auth)
目录,再分别创建sign-up
和sign-in
目录以及其下的page.tsx
:
export default function SignUpPage () {
return (
<div>
Sign Up page
</div>
)
}
export default function SignInPage () {
return (
<div>
Sign in page
</div>
)
}
现在我们分别访问 http://localhost:3000/sign-up 和 http://localhost:3000/sign-in ,就可以看到:
而如果我们访问 http://localhost:3000/auth/sign-in ,就会得到一个404页面:
这就说明auth并没有作为路由地址的节点发挥作用。
另外,虽然我们使用了隐藏路由,但是如果我们在(auth)
目录下创建一个layout.tsx
文件,这个布局也会作用到(auth)
目录下的所有页面。所以这个隐藏路由不仅起到了归类的作用,还同样发挥了共享布局的功能。
想要了解更多Next.js路由相关的内容,大家可以自行访问Next.js官方文档 。
组件目录
由于我们项目的每个页面或者每个路由组都需要用到一些组件,那这些组件文件一般放在哪里呢?
我们一般的习惯是将那些整个项目全局都可能用到的组件放在项目根目录下的components
目录里,也就是shadcn ui组件所在的上一级目录。
最好不要把我们自定义的组件放在components/ui
目录下,这个目录我们默认就放shadcn的组件,以免造成混淆。
而对于一些特定路由组或者特定页面会用到的组件,我们建议在该路由组或页面所在的目录下创建一个名为_components
的目录,这样命名Next.js就不会把这个目录识别为一个子路由,而是会在路由解析时直接忽略。
关于.next
目录
你可能已经注意到了,在项目根目录下有一个颜色较浅的.next
目录,这个目录是项目运行中产生的一些缓存文件。如果你在运行项目是出现了一些问题,可以把这个目录删掉,然后重新运行npm run dev
,就会重新生成这个目录。
关于Next.js框架的路由机制我们就简单介绍到这里。如果直接看官方文档过于枯燥看不下去的话,不用担心,让我们一边实操一边学习,很快就能对这些机制有一些直观的了解。