后端转全栈之Next.js文件约定

本文概括

  • Page Router vs App Router :老版用 pages 目录,新版用 app 目录并通过约定文件定义路由和特殊功能。
  • page.tsx:定义具体页面,对应一个路由。
  • layout.tsx :定义可复用的页面布局,子页面会被包裹其中,根布局必须包含 htmlbody
  • template.tsx:类似布局但不会保留状态,每次路由切换都会重新渲染。
  • loading.tsx:在页面数据加载时显示的过渡界面。
  • error.tsx :客户端错误边界组件,捕获运行时错误并支持 reset 重试。
  • global-error.tsx:根目录的全局错误捕获页面。
  • not-found.tsx :定义 404 页面,用于未匹配路由或显式调用 notFound()

老版本的Next.js使用的是Page Router,在 pages 目录下,每个js文件就是一个路由,这就导致一些组件不能写在 pages 目录下,新版本换成了App Router ,文件放在 app目录下,目录下的 page.tsx 就是代表一个路由,Next.js约定了一些特殊的文件:

布局(layout.tsx)、模板(template.tsx)、加载状态(loading.tsx)、错误处理(error.tsx)、404(not-found.tsx)页面(page.tsx)

页面page.tsx

每个目录下的 page.tsx 会映射到一个路由,需要导出一个默认函数,例如:

javascript 复制代码
export default function Page(){
	return <>Next.js</>
}

布局layout.tsx

layout.tsx 文件导出一个React组件,接受 chidren 作为参数,表示的是子页面内容,子页面会拥有layout里的布局,也就是layout会包裹着page页面

javascript 复制代码
export default function Layout({ children }: { children: React.ReactNode }) {
    return (
        <div>
            <h1>Test Layout</h1>
            {children}
        </div>
    )
}

根布局要求:

  • 必须有根布局 app/layout.tsx
  • 必须包含 htmlbody 标签

模版template.tsx

模版和layout类似,会包裹每个页面,但是和layout的区别是,模版不会维持状态,每次进入一个新的路由都会重新初始化,模版会被layout包裹起来。

例如在layout文件里写一个 表单,那么通过Link跳转到子路由,表单里的内容不会变,如果是使用template,那么就会重新渲染,表单里的数据消失

例如 layout.tsx

javascript 复制代码
'use client'
import React, { useState } from 'react'

export default function Layout({ children }: { children: React.ReactNode }) {
    const [count, setCount] = useState(0)

    return (
        <div>
            <h1>Test Layout</h1>
            <>Layout Count: {count}</>
            <button onClick={() => setCount(count + 1)}>Layout数字增加</button>
            <br />
            {children}
        </div>
    )
}

template.tsx

javascript 复制代码
'use client'
import React, { useState } from 'react'
export default function Template({ children }: { children: React.ReactNode }) {
    const [count, setCount] = useState(0)

    return (
        <div>
            <>Template Count: {count}</>
            <h1>Test Template</h1>
            <button onClick={() => setCount(count + 1)}>Template数字增加</button>
            {children}
        </div>
    )
}

当我们增加了数字之后,在test目录路由下跳转,会发现layout里的数字不变,template里的数字会清空

加载loading.tsx

loading.tsx 是加载页面,例如:

javascript 复制代码
export default function Loading() {
    return (
        <div>
            <h1>Loading</h1>
        </div>
    )
}

这样在 page.tsx 加载数据的时候,在没拿到数据之前,就会显示loading的内容:

javascript 复制代码
import Link from 'next/link'

async function getUser() {
    await new Promise((resolve) => setTimeout(resolve, 2000))
    return {
        name: 'cxk',
    }
}

export default async function Page() {
    const { name } = await getUser()
    return (
        <div>
            <Link href="/test/test2">跳转Page2</Link>
            <Link href="/test">跳转Page</Link>
            {name}
        </div>
    )
}

如果一个目录下有很多约定的文件,那么他们的层级是:

nextjs.org/docs/app/ge...

Next.js文件约定

错误error.tsx

错误页面必须是客户端组件, error.tsx 接受一个error和reset

  • error: Error

    捕获到的错误对象,可能带 digest(Next.js 内部生成的唯一标识符)。

  • reset: () => void

    调用它可以 重置错误边界,让 Next.js 重新尝试渲染页面(比如在用户点 "Retry" 按钮时)。

javascript 复制代码
'use client'

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
    const handleReset = () => {
        console.log('reset')
        reset()
    }
    return (
        <div>
            <h1>Error</h1>
            <p>{error.message}</p>
            <button onClick={handleReset}>Reset</button>
        </div>
    )
}

可以在页面获取数据的时候抛出异常试试:

javascript 复制代码
async function getUser() {
    await new Promise((resolve) => setTimeout(resolve, 2000))
    throw new Error('test error')
    return {
        name: 'cxk',
    }
}

export default async function Page() {
    const { name } = await getUser()
    return (
        <div>
            <Link href="/test/test2">跳转Page2</Link>
            <Link href="/test">跳转Page</Link>
            {name}
        </div>
    )
}

注意:错误边界不可以捕获同级的layout和tempalte,必须在父级的error.tsx去捕获,因为 ErrorBoundary 被Layout和Template包裹了。

根目录的捕获可以使用 global-error.tsx 进行捕获

404not-found.tsx

未找到页面会显示404页面的内容,触发情况主要有两种:

  • 组件调用 notFound 函数
  • 路由地址不匹配
相关推荐
光影少年12 小时前
react生态
前端·react.js·前端框架
ObjectX前端实验室1 天前
【react18原理探究实践】React Effect List 构建与 Commit 阶段详解
前端·react.js
ObjectX前端实验室1 天前
【react18原理探究实践】更新阶段 Render 与 Diff 算法详解
前端·react.js
小刘鸭地下城2 天前
Next.js 性能优化指南
next.js
ObjectX前端实验室2 天前
【react18原理探究实践】render阶段【首次挂载】
前端·react.js
ObjectX前端实验室2 天前
【react18原理探究实践】组件的 props 和 state 究竟是如何确定和存储的?
前端·react.js
明里人2 天前
React 状态库:Zustand 和 Jotai 怎么选?
前端·javascript·react.js
訾博ZiBo2 天前
为什么我的 React 组件会无限循环?—— 一次由 `onClick` 引发的“惨案”分析
前端·react.js
訾博ZiBo2 天前
React状态更新之谜:为何大神偏爱`[...arr]`,而非`arr.push()`?
react.js
訾博ZiBo2 天前
告别 v-model 焦虑:在 React 中优雅地处理『双向绑定』
前端·react.js