大家好,我是water,欢迎关注我的公众号:前端进阶说,最近在看Nextjs
相关内容就做个学习记录,希望对你有帮助。
前言
next13
之后官方推荐 app router
。规则上是在 app
文件夹下会根据文件目录生成路由。看下路由的生成规则和一些注意事项。
路由生成规则
在 next
的文件路由生成规则中,文件夹会用来定义生成路由。每一个文件夹会生成 url
路由的一个路由段。所以可以通过嵌套文件夹的方式生成多段路由。
如果想要生成一个可以访问的路由,需要在文件夹下创建一个page.tsx
文件。
tsx
export default function Page() {
return <div>page</div>
}
这样就可以访问生成的理由了,比如在 app
文件夹下创建一个page.tsx
文件,就可以通过/
访问到这个页面。因为 app
文件夹是生成根路由的。
页面和布局
页面是在路由文件夹下定义一个page.tsx
文件。这样这个文件夹就会编译成路由。而且必须以page.tsx
命名,否则不会生成路由。文件中直接导出一个组件就可以了。
tsx
export default function Page() {
return <div>page</div>
}
布局可以在多个页面共用,也可以为单独的页面自定义布局。布局是在路由文件夹下定义一个layout.tsx
文件。而且必须以layout.tsx
命名。文件中导出一个组件,组件接受一个children
属性,这个属性就是页面的内容。
tsx
export default function Layout({ children }) {
return (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
)
}
在 app
中定义的布局为根布局有需要注意的点,这个根布局文件必须包含html
和body
标签。而且必须有根布局,否则会报错。
tsx
export default function Layout({ children }) {
return (
<html>
<body>
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
</body>
</html>
)
}
嵌套页面中可以拥有自己的布局,在目录文件夹中建立布局文件,这样就会出现嵌套布局。那么文件夹的目录结构会是这样的。
最后组成的嵌套布局会是这样的。
模版
模版是在路由文件夹下定义一个template.tsx
文件。而且必须以template.tsx
命名。文件中导出一个组件,组件接受一个children
属性,这个属性就是页面的内容。
tsx
export default function Template({ children }) {
return (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
)
}
template.js
和 layout.js
在实际使用中并没有任何固定的不一样,它们都是自定义的文件名,它们的作用与内容主要取决于开发者如何使用这两个文件。
理论上,它们都可以用作定义公共布局或公共组件。
一般来说,layout.js
通常用于存储页面或应用程序的通用布局。比如你可能有一个统一的顶栏和底栏,围绕中间内容,那么你就可以把这两个部分放在 layout.js
文件中。
而 template.js
则更加灵活一些,它可以作为公共代码模板库,存放各种各样的代码模板,比如表单模板,列表模板等等,然后在不同的页面里按需引入。
但是具体的使用方式,还需要根据实际的需求和开发习惯来定。这两个概念并没有严格的定义,所以开发者可以根据自己的需要自由地将它们用做任何他们需要的用处。
一般来说模版出现在页面和布局之间
tsx
export default function Layout({ children }) {
return (
<div>
<div>header</div>
<div>
<Template>{children}</Template>
</div>
<div>footer</div>
</div>
)
}
修改上的属性参数
在 nextjs
中,可以通过Metadata
来修改<head />
上的属性参数。
tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js'
}
export default function Page() {
return <div>page</div>
}
链接和导航
在 nextjs
中,可以通过Link
组件来实现路由跳转。
tsx
import Link from 'next/link'
export default function Page() {
return (
<div>
<Link href="/about">
<a>About</a>
</Link>
</div>
)
}
可以通过usePathname
来获取当前路由。
tsx
import { usePathname } from 'next/navigation'
export default function Page() {
const pathname = usePathname()
return <div>Current path: {pathname}</div>
}
可以通过#hash
来跳转到指定的锚点。
tsx
import Link from 'next/link'
export default function Page() {
return (
<div>
<Link href="/about#team">
<a>About</a>
</Link>
</div>
)
}
默认情况下的跳转会保持页面的滚动位置,如果想要禁用,可以通过scroll
属性来实现。
tsx
import Link from 'next/link'
export default function Page() {
return (
<div>
<Link href="/about" scroll={false}>
<a>About</a>
</Link>
</div>
)
}
导航路由跳转还可以通过 useRouter
来实现。
tsx
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<div>
<button onClick={() => router.push('/about')}>About</button>
</div>
)
}
路由分组
对路由进行分组并不会影响原有路由的访问,只是对文件进行了分组,可以通过文件夹的方式来实现。使用(grop)
的方式来命名分组的包裹文件夹。这样就可以实现分组路由。
每个分组可以公用一个布局,也可以自定义布局。如果想要自定义布局,可以在分组文件夹下创建一个layout.tsx
文件。而且必须以layout.tsx
命名。文件中导出一个组件,组件接受一个children
属性,这个属性就是页面的内容。
分组下的页面可以共用同一个分组的布局,当然分组可以有多个根布局,这样就可以实现多种布局的组合。但是如果想要使用这样的方式就需删除 app
中的根布局,在各自的分组建立布局文件。
动态路由
动态路由规则是用中括号[]
包裹路由段。这样就可以实现动态路由。比如/post/[id]/page.tsx
就可以匹配到/post/1
这样的路由。
tsx
export default function Page({ params }: { params: { id: string } }) {
return <div>My Post: {params.id}</div>
}
其中路由参数会被传递到页面的 props
中。看下理由生成的例子
如果是多级观松匹配生成,那么就在参数前面加上省略号...
。比如/post/[...id]/page.tsx
就可以匹配到/post/1/2/3
这样的路由。
如果是可选的多级匹配参数,可以使用两个中括号来包裹参数。比如/post/[[...id]]/page.tsx
就可以匹配到/post
和/post/1
和/post/1/2/3
这样的路由。和上面的区别就是如果没有参数会匹配到/post
这个路由。
loading ui 显示
可以在路由文件夹下定义一个loading.tsx
文件。而且必须以loading.tsx
命名。
tsx
export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}
文件目录
这样在访问这个路由的开始会先显示这个 loading
的 ui
,等到路由加载完成后才会显示路由的内容。
error 错误显示页面
通过 error
页面来进行错误捕获,在文件夹中定义error.tsx
文件,那么就会在当前路径下进行错误捕获
tsx
'use client' // Error components must be Client Components
import { useEffect } from 'react'
export default function Error({
error,
reset
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
)
}
错误页面的工作原理是自动创建 react
的错误捕获
通过 reset
方法有些短暂的错误可以重试得以解决。
在嵌套路由中每个文件夹都有错误页面的话,就会生成嵌套的错误捕获,以最近的先捕获
由于 app/error.tsx
不会捕获 layout.tsx
和 template.tsx
中的错误,所以如果要处理这种根组件中的错误,可以使用global-error.tsx
tsx
'use client'
export default function GlobalError({
error,
reset
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
)
}
并行路线渲染
并行的路由渲染,可以同时或有条件地在同一布局中渲染一个或多个页面。
并且并行路由可以对每个定义独立的错误和加载状态
还可以通过条件的判断有条件的选择渲染内容
约定命名规则
并行路由是使用命名插槽创建的,文件夹的命名规则是@folder
,并且命名作为 props
传递给同级别路由。
注意 命名插槽不是路由段,不会影响
URL
结构。文件路径/@team/members
可以通过 访问/members
。
上面就是定义了两个并行路由页面。在 app/layout.js
接受@analytics
和@team
两个命名插槽。并且可以在 layout.js
中使用 props.analytics
和 props.team
。
tsx
export default function Layout(props: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
)
}
注意 该
children prop
是一个隐式插槽,不需要映射到文件夹。这意味着app/page.js
相当于app/@children/page.js
。
如果并行路由中有一个路由不匹配,那么就会去匹配 default.js
useSelectedLayoutSegment
和 useSelectedLayoutSegments
useSelectedLayoutSegment
和 useSelectedLayoutSegments
两个 hook
可以获取当前选中的路由。
tsx
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default async function Layout(props: {
//...
auth: React.ReactNode
}) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
当 url
为 login
时,loginSegments
等于字符串"login"
Modal
并行路由可用于渲染模态框。
一般时通过匹配路由来显示模态框。比如'/login'
。
tsx
export default async function Layout(props: {
// ...
auth: React.ReactNode
}) {
return <div>{props.auth}</div>
}
tsx
import { Modal } from 'components/modal'
export default function Login() {
return (
<Modal>
<h1>Login</h1>
</Modal>
)
}
tsx
export default function Default() {
return null
}
通过调用或使用组件<Link href="/Login">
或者router.back()
tsx
'use client'
import { useRouter } from 'next/navigation'
import { Modal } from 'components/modal'
export default async function Login() {
const router = useRouter()
return (
<Modal>
<span onClick={() => router.back()}>Close modal</span>
<h1>Login</h1>
</Modal>
)
}
如果你想导航到其他地方并关闭模态框
tsx
export default function CatchAll() {
return null
}
通过条件渲染
tsx
import { getUser } from '@/lib/auth'
export default function Layout({
dashboard,
login
}: {
dashboard: React.ReactNode
login: React.ReactNode
}) {
const isLoggedIn = getUser()
return isLoggedIn ? dashboard : login
}
路由拦截
路由拦截允许你在当前布局内应用程序的路由加载其他路由,而且是显示内容而不需要用户切换到相应的路由上。
如果点击分享的 url
或者刷新页面,就应该显示完整的页面
路由拦截的定义规则
路由拦截用(..)来定义
- (.) 匹配同一级别的路由段
- (..) 匹配上一级别的路由段
- (..)(..) 匹配上面两级的路由段
- (...) 匹配根 app 目录中的路由段
使用 modal 的例子
小结
通过文件目录与命名规则编译出对应的路由页面。体现出了和以前版本的差别。通过这节基本可以搭建一个简单的页面项目了,还有一些进阶的用法后面在学习了,希望对你有帮助,如果觉得有帮助欢迎点个关注