Nextjs系列——新版本路由的使用

大家好,我是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 中定义的布局为根布局有需要注意的点,这个根布局文件必须包含htmlbody标签。而且必须有根布局,否则会报错。

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.jslayout.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 />
}

文件目录

这样在访问这个路由的开始会先显示这个 loadingui,等到路由加载完成后才会显示路由的内容。

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.tsxtemplate.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.analyticsprops.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

useSelectedLayoutSegmentuseSelectedLayoutSegments

useSelectedLayoutSegmentuseSelectedLayoutSegments 两个 hook 可以获取当前选中的路由。

tsx 复制代码
'use client'

import { useSelectedLayoutSegment } from 'next/navigation'

export default async function Layout(props: {
  //...
  auth: React.ReactNode
}) {
  const loginSegments = useSelectedLayoutSegment('auth')
  // ...
}

urllogin 时,loginSegments 等于字符串"login"

并行路由可用于渲染模态框。

一般时通过匹配路由来显示模态框。比如'/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 目录中的路由段

小结

通过文件目录与命名规则编译出对应的路由页面。体现出了和以前版本的差别。通过这节基本可以搭建一个简单的页面项目了,还有一些进阶的用法后面在学习了,希望对你有帮助,如果觉得有帮助欢迎点个关注

相关推荐
虾球xz28 分钟前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇33 分钟前
HTML常用表格与标签
前端·html
疯狂的沙粒37 分钟前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员1 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐1 小时前
前端图像处理(一)
前端
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒1 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪1 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背1 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M1 小时前
node.js第三方Express 框架
前端·javascript·node.js·express