当前位置: 首页 > news >正文

php做网站好学吗河北工程大学事件

php做网站好学吗,河北工程大学事件,wordpress获取文章作者id,深圳建设工程项目网站路由 路由基础知识 每个应用程序的骨架都是路由。本页将向你介绍互联网路由的基本概念以及如何在 Next.js 中处理路由。 术语 首先#xff0c;你将在整个文档中看到这些术语的使用情况。以下是一个快速参考#xff1a; 树#xff08;Tree#xff09;#xff1a;用于可…路由 路由基础知识 每个应用程序的骨架都是路由。本页将向你介绍互联网路由的基本概念以及如何在 Next.js 中处理路由。 术语 首先你将在整个文档中看到这些术语的使用情况。以下是一个快速参考 树Tree用于可视化层次结构的约定。例如具有父组件和子组件的组件树、文件夹结构等子树Subtree树的一部分从新的根开始第一个到叶子结束最后一个根Root树或子树中的第一个节点例如根布局root layout叶Leaf子树中没有子级的节点例如URL 路径中的最后一段 URL 段由斜杠分隔的 URL 路径的一部分URL 路径位于域之后的 URL 的一部分由段组成 app 路由器 在版本 13 中Next.js 引入了一个基于 React Server Components 的新 App Router它支持共享布局、嵌套路由、加载状态、错误处理等。 App Router 在一个名为 app 的新目录中工作。app 目录与 pages 目录一起工作以允许增量采用。这允许你将应用程序的某些路由选择为新行为同时将其他路由保留在 pages 目录中以用于以前的行为。如果你的应用程序使用 pages 目录请参阅 Pages Router 文档。 需要知道App Router 优先于 Pages Router。跨目录的路由不应解析为相同的 URL 路径这将导致生成时错误以防止冲突。 默认情况下app 内部的组件是 React Server Components。这是一种性能优化允许你轻松采用它们还可以使用 Client Components。 建议如果你才开始使用服务器组件请查看 Server 页面。 文件夹和文件的角色 Next.js 使用基于文件系统的路由器其中 文件夹用于定义路由。路由是嵌套文件夹的单一路径遵循文件系统层次结构从根文件夹向下到包括 page.js 文件的最终叶文件夹。请参见Defining Routers。文件用于创建为路由段显示的 UI。请参阅特殊文件。 路由段 路由中的每个文件夹都表示一个路由段。每个路由段都映射到 URL 路径中的相应段。 嵌套路由 若要创建嵌套路由可以将文件夹嵌套在彼此内部。例如你可以通过在 app 目录中嵌套两个新文件夹来添加新的 /dashboard/settings 路由。 dashboard/settings 路由由三个部分组成 /根段dashboard段settings叶段 文件约定 Next.js 提供了一组特殊的文件来创建嵌套路由中具有特定行为的 UI 文件名说明layout段及其子级的共享 UIpage路由的唯一 UI并使路由可公开访问loadingLoading 段及其子级的 UInot-foundNot found 段及其子级的 UIerror段及其子级的错误 UIglobal-error全局错误 UIroute服务器端 API 端点template专门的重新渲染布局 UIdefault并行路由的回退 UI 需要知道.js、.jsx 或 .tsx 文件扩展名可以用于特殊文件。 组件层次结构 路由段的特殊文件中定义的 React 组件在特定层次中渲染 layout.jstemplate.jserror.jsReact 错误边界loading.jsReact suspense 边界not-found.jsReact 错误边界page.js 或嵌套 layout.js 在嵌套路由中段的组件将嵌套在其父段的组件内。 合并 除了特殊文件外你还可以选择将自己的文件如组件、样式、测试等合并到 app 目录中的文件夹中。 这是因为当文件夹定义路由时只有 page.js 或 route.js 返回的内容是可公开寻址的。 了解有关项目组织和托管的更多信息。 高级路由模式 App Router 还提供了一组约定以帮助你实现更高级的路由模式。其中包括 并行路由允许你在同一视图中同时显示两个或多个可以独立导航的页面。你可以将它们用于具有自己的子导航的拆分视图。例如Dashboards。拦截路由允许你拦截一条路由并将其显示在另一条路由的上下文中。当保留当前页面的上下文很重要时可以使用这些内容。例如在编辑一个任务或在提要中展开照片时查看所有任务。 这些模式允许你构建更丰富、更复杂的 UI使小团队和个人开发人员实现的历史上复杂的功能民主化。 定义路由 本页将指导你如何在 Next.js 应用程序中定义和组织路由。 创建路由 Next.js 使用基于文件系统的路由器其中文件夹用于定义路由。 每个文件夹代表一个映射到 URL 段的路由段。若要创建嵌套路由可以将文件夹嵌套在彼此内部。 一个特殊的 page.js 文件用于使路由段可以公开访问。 在本例中/dashboard/analytics URL 路径不可公开访问因为它没有相应的 page.js 文件。此文件夹可用于存储组件、样式表、图像或其他共有文件。 b.js、.jsx 或 .tsx 文件扩展名可以用于特殊文件。 创建 UI 特殊的文件约定用于为每个路由段创建 UI。最常见的是显示路由唯一 UI 的页面以及显示跨多个路由共享的 UI 的布局。 例如要创建第一个页面请在 app 目录中添加 page.js 文件然后导出 React 组件 // app/page.tsxexport default function Page() {return h1Hello, Next.js!/h1 }页面与布局 Next.js 13 中的 App Router 引入了新的文件约定可以轻松创建页面、共享布局和模板。本页将指导你如何在 Next.js 应用程序中使用这些特殊文件。 页面 页面是路由唯一的 UI。你可以通过从 page.js 文件导出组件来定义页面。使用嵌套文件夹定义路由并使用 page.js 文件使路由可以公开访问。 通过在 app 目录中添加 page.js 文件来创建您的第一个页面 // app/page.tsx// app/page.tsx 是 / 路径的 UI export default function Page() {return h1Hello, Home page!/h1 }// app/dashboard/page.tsx// app/dashboard/page.tsx 是 /dashboard 路径的 UI export default function Page() {return h1Hello, Dashboard Page!/h1 }需要知道 页面始终是路由子树的叶子页面的文件类型应该为 .js、.jsx、.tsx需要一个 page.js 文件才能公开一个路由段默认情况下页面是 Server Components但可以设置为 Client Component页面可以获取数据请查看 Data Fetching 部分了解更多信息 布局 布局是在多个页面之间共享的 UI。在导航时布局会保留状态保持交互式并且不会重新渲染。布局也可以嵌套。 你可以通过 default 导出从 layout.js 文件导出 React 组件来定义布局。组件应接收 children prop该 prop 将在渲染过程中填充子布局如果存在或子页面。 // app/dashboard/layout.tsxexport default function DashboardLayout({children, // children 是一个页面或嵌套布局 }: {children: React.ReactNode }) {return (section{/* 在此处包括共享 UI例如header 或 sidebar */}nav/nav{children}/section) }需要知道 最顶层的布局叫做根布局这个必需的布局被一个应用中的所有页面共享根布局必须包含 html 和 body 标签任何路由段可以定义它自己的布局这些布局将会在该路由的所有页面中共享默认情况下一个路由中的布局可以被嵌套每个父布局都使用 React 的 children prop 将子布局包裹在其下方可以使用路由组Route Groups来选择特定的路由进和出共享布局默认情况下布局是服务器组件但可以设置为客户端组件布局可以获取数据查看 Data Fetching 部分了解更多信息无法在父、子布局之间传递数据但是可以在一个路由中多次请求相同的数据并且 React 会自动对请求进行重复数据删除而不会影响性能布局无法访问自身路径下的路由为了访问所有的路由可以在客户端组件中使用 useSelectedLayoutSegment 或者 useSelectedLayoutSegments布局的文件类型应该为 .js、.jsx 或 .tsx可以在同一个文件夹中定义 layout.js 和 page.js 文件布局将会包裹页面 根布局必需 根布局在 app 目录的顶层定义并应用于所有路由。此布局使你能够修改从服务器返回的初始 HTML。 // app/layout.tsxexport default function RootLayout({children, }: {children: React.ReactNode }) {return (html langenbody{children}/body/html) }需要知道 该 app 目录必须包含根布局根布局必须定义 html 和 body 标签因为 Next.js 不会自动创建它们可以使用内置的 SEO 支持来管理 head HTML 元素例如title 元素可以使用路由组来创建多个根布局在这查看一个例子默认情况下根布局是一个服务器组件并且不能设置为客户端组件 从 pages 目录迁移根布局替换 _app.js 和 _document.js 文件。查看迁移导游。 嵌套布局 文件夹内定义的布局例如app/dashboard/layout.js应用于特定的路由段例如acme.com/dashboard并在这些段处于活动状态时进行渲染。默认情况下文件层次结构中的布局是嵌套的这意味着它们通过其 children prop 包装子布局。 // app/dashboard/layout.tsxexport default function DashboardLayout({children, }: {children: React.ReactNode }) {return section{children}/section }需要知道 仅仅根布局可以包含 html 和 body 标签 如果你将上面的两个布局组合在一起根布局app/layout.js将包裹 dashboard 布局app/dashboard/layout.js它将包裹 app/dashboard/* 内的路由段。 这两种布局将按如下方式嵌套 你可以使用路由组在共享布局中选择特定的路由也可以选择不选择。 模板 模板与布局的相似之处在于它们包裹每个子布局或页面。与在路由上保持不变并保持状态的布局不同模板在导航中为其每个子级创建一个新实例。这意味着当用户在共享模板的路由之间导航时将挂载组件的新实例重新创建 DOM 元素不保留状态并重新同步效果。 在某些情况下你可能需要这些特定的行为模板将是比布局更合适的选择。例如 依赖 useEffect例如记录页面视图和 useState例如每页反馈表单的功能。 更改默认框架行为。例如布局内的 Suspense Boundaries 仅在第一次加载布局时显示回退而在切换页面时不显示回退。对于模板回退显示在每个导航上。 可以通过从 template.js 文件导出默认的 React 组件来定义模板。这个组件应该接收一个 children prop。 // app/template.tsxexport default function Template({ children }: { children: React.ReactNode }) {return div{children}/div }在嵌套方面template.js 将会在布局及其子节点之间渲染。 // OutputLayout{/* 注意这个模板存在一个唯一的 key */}Template key{routeParam}{children}/Template /Layout修饰 head 在 app 目录可以使用内置的 SEO 支持修饰 head HTML 元素例如title 和 meta 标签。 元数据可以在 layout.js 或者 page.js 文件中通过导出一个 metadata 对象或 generateMetadata 函数而被定义。 // app/page.tsximport { Metadata } from nextexport const metadata: Metadata {title: Next.js, }export default function Page() {return ... }需要知道不应该手动将 head 标签添加到根布局中相反应该使用 Metadata API它可以自动处理高级请求例如流式和消除重复 head 元素。 了解有关 API 参考中可用元数据选项的更多信息。 页面和布局的区别 页面是独有的布局是公共的可以被后代组件访问 链接和导航 在 Next.js 中有两种方法可以在路由之间导航 使用 Link 组件使用 useRouter hook 本页将介绍如何使用 Link、useRouter()并深入了解导航的工作原理。 link 组件 Link 是一个内置组件它扩展了 HTML a 标签以提供路由之间的预获取和客户端导航。这是 Next.js 中在路由之间导航的主要方式。 你可以通过从 next/link 导入并将 href prop 传递给组件来使用它 // app/page.tsximport Link from next/linkexport default function Page() {return Link href/dashboardDashboard/Link }你还可以将其他可选 props 传递给 link。有关更多信息请参阅 API 参考资料。 例子 链接到动态段 链接到动态段时可以使用模板文字和插值来生成链接列表。例如要生成博客文章列表 // app/blog/PostList.jsimport Link from next/linkexport default function PostList({ posts }) {return (ul{posts.map((post) (li key{post.id}Link href{/blog/${post.slug}}{post.title}/Link/li))}/ul) }切换活动链接 你可以使用 usePathname() 来确定链接是否处于活动状态。例如要将类添加到活动链接可以切换当前 pathname 来匹配 href // app/components/links.tsxuse clientimport { usePathname } from next/navigation import Link from next/linkexport function Links() {const pathname usePathname()return (navulliLink className{link ${pathname / ? active : }} href/Home/Link/liliLinkclassName{link ${pathname /about ? active : }}href/aboutAbout/Link/li/ul/nav) }滚动到 id Next.js App Router 的默认行为是滚动到新路线的顶部或者保持滚动位置以进行前后导航。 如果你想在导航中滚动到特定的 id你可以在 URL 后面添加一个 # 哈希链接或者只将一个哈希链接传递给 href prop。这是可能的因为 Link 渲染到 a 元素。 Link href/dashboard#settingsSettings/Link// 转换后 a href/dashboard#settingsSettings/a禁用滚动恢复 Next.js App Router 的默认行为是滚动到新路由的顶部或者保持滚动位置以进行前后导航。如果你想禁用此行为可以将 scroll{false} 传递给 Link 组件或者将 scroll: false 传递给 router.push() 或 router.replace()。 // next/link Link href/dashboard scroll{false}Dashboard /Link// useRouter import { useRouter } from next/navigationconst router useRouter()router.push(/dashboard, { scroll: false })useRouter() hook useRouter() hook 允许以编程方式更改路由。 此 hook 只能在客户端组件中使用并从 next/navigation 导入。 // app/page.jsuse clientimport { useRouter } from next/navigationexport default function Page() {const router useRouter()return (button typebutton onClick{() router.push(/dashboard)}Dashboard/button) }有关 useRouter 方法的完整列表请参阅 API 参考资料。 建议使用 Link 组件在路由之间导航除非对使用 useRouter 有特定要求。 路由和导航的工作原理 App Router 使用混合方法进行路由和导航。在服务器上你的应用程序代码会自动按路由段进行代码分割。在客户端上Next.js 预获取并缓存路由段。这意味着当用户导航到新路由时浏览器不会重新加载页面只会重新渲染更改的路由段从而提高导航体验和性能。 1. 预获取 预获取是在用户访问前在后台预加载路由的一个方式。 Next.js 中有两种预获取路由的方式 Link 组件路由在出现在用户的视图前会自动被预获取。预获取发生在页面第一次被加载或者通过滚动进入视图时。 router.prefetch()useRouter hook 可以被用来以编程方式预获取路由。 Link 组件的预获取行为在静态路由和动态路由之间的表现不同 静态路由prefetch 默认为 true。整个路由将会被预获取和缓存。动态路由prefetch 默认为 automatic自动。只有共享布局向下直到第一个 loading.js 文件被预获取并缓存了 30s。这降低了获取整个动态路由的成本并且意味着可以显示即时加载状态以便向用户提供更好的视觉反馈。 可以通过将 prefetch prop 设置为 false 来禁用预获取。 有关更多信息请参阅 link API 参考资料。 需要知道预取在开发中不启用只在生产中启用。 2. 缓存 Next.js 有一个内存中的客户端缓存称为 Router Cache。当用户在应用中导航时预获取路由段和访问路由的 React 服务器组件有效负载存储在缓存中。 这意味着在导航时将尽可能地重用缓存而不是向服务器发起一个新的请求——通过减少请求和数据传输的数量来提高性能。 了解有关 Router Cache 如何工作以及如何配置它的更多信息。 3. 局部渲染 局部渲染意味着只有在导航时发生更改的路由段才会在客户端上重新渲染并且任何共享段都会保留。 例如当在两个同级路由/dashboard/settings 和 /dashboard/analytics之间导航时将渲染 settings 和 analytics 页面并保留共享的 dashboard 布局。 如果没有局部渲染每个导航都会导致整个页面在服务器上重新渲染。仅渲染发生更改的段可以减少传输的数据量和执行时间从而提高性能。 4. 软导航 默认情况下浏览器在页面之间执行硬导航。这意味着浏览器会重新加载页面并重置 React 状态如应用程序中的 useState hook以及浏览器状态如用户的滚动位置或聚焦元素。然而在 Next.js 中App Router 使用软导航。这意味着 React 只渲染已更改的片段同时保留 React 和浏览器状态并且不重新加载整个页面。 5. 前进/后退导航 默认情况下Next.js 会保留前进和后退导航的滚动位置并重复使用 Router Cache 中的路由。 路由组 在 app 目录中嵌套文件夹通常是映射到 URL 路径。然而可以标记文件作为路由组以防止文件夹包含在路由的 URL 路径中。 这允许将路由段和项目文件组织到逻辑组中而不会影响 URL 路径结构。 路由组可用于 将路由分组例如按站点部分、意图或团队在同一级别的路由段中允许嵌套布局 在相同的段中创建多个嵌套布局包括多个根布局在公共段中添加一个布局到路由子集 约定 可以通过将文件夹的名称括在括号中来创建路由组例如(folderName)。 例子 在不影响 URL 路径的情况下组织路由 要在不影响 URL 的情况下组织路由请创建一个组将相关路由放在一起。括号中的文件夹将从 URL 中省略例如(marketing) 或 (shop)。 即使 (marketing) 和 (shop) 内部的路由共享相同的 URL 层次结构你也可以通过在每个组的文件夹中添加 layout.js 文件来为它们创建不同的布局。 将特定段选择到布局中 要将特定路由选择到布局中请创建一个新的路由组例如(shop)并将共享相同布局的路由移动到路由组中例如account 和 cart。路由组外的路由将不会共享这些布局例如checkout。 创建多个根布局 要创建多个根布局请删除顶层的 layout.js 文件并在每个路由组中添加一个 layout.js 文件。这对于将应用程序划分为具有完全不同的 UI 或体验的部分非常有用。html 和 body 标签需要添加到每个根布局中。 在上面的例子中(marketing) 和 (shop) 都有自己的根布局。 需要知道 路由组的命名除了用于组织之外没有其他特殊意义。它们不会影响 URL 路径。包含路由组的路由不应该被解析为与其他路由相同的 URL 路径。例如由于路由组不会影响 URL 结构(marketing)/about/page.js 和 (shop)/about/page.js 都会被解析为 /about 从而导致错误。如果你使用多个没有顶级 layout.js 文件的根布局你的主页 page.js 文件应该在其中一个路由组中定义。例如app/(marketing)/page.js。在多个根布局之间导航将导致整个页面加载与客户端导航相反。例如从使用 app/(shop)/layout.js 的 /cart 导航到使用 app/(marketing)/layout.js 的 /blog 将导致整个页面加载这仅适用于多个根布局。 动态路由 当你事先不知道确切的段名称并且希望根据动态数据创建路由你可以使用在请求时填写或在构建时预渲染的动态段Dynamic Segments。 约定 可以通过将文件夹名称括在方括号中来创建动态段Dynamic Segments形如 [folderName]例如[id] 或 [slug]。 动态段作为 params prop 传递给 layout、 page、route 和 generateMetadata 函数。 例子 例如博客可以包括以下路由 app/blog/[slug]/page.js其中 [slug] 是博客文章的动态段。 // app/blog/[slug]/page.tsxexport default function Page({ params }: { params: { slug: string } }) {return divMy Post: {params.slug}/div }路由示例 URLparamsapp/blog/[slug]/page.js/blog/a{ slug: a }app/blog/[slug]/page.js/blog/b{ slug: b }app/blog/[slug]/page.js/blog/c{ slug: c } 请参阅 generateStaticParams() 页面了解如何为段生成参数params。 需要知道动态段相当于 pages 目录的动态路由。 生成静态参数 generateStaticParams 函数可以与动态路由段结合使用以在构建时静态生成路由而不是在请求时按需生成。 // app/blog/[slug]/page.tsxexport async function generateStaticParams() {const posts await fetch(https://.../posts).then((res) res.json())return posts.map((post) ({slug: post.slug,})) }generateStaticParams 函数的主要优点是它可以智能地检索数据。如果使用 fetch 请求在 generateStaticParams 函数中获取内容那么这个请求会自动保存。这意味着在多个 generateStaticParams、布局Layouts、页面Pages中具有相同参数的 fetch 请求将只执行一次这减少了构建时间。 如果要从 pages 目录进行迁移请使用迁移指南。 有关更多信息和高级用例请参阅 generateStaticParams 服务器功能文档。 捕获所有段 通过在括号 [...folderName] 内添加省略号可以扩展动态段以捕获所有后续分段。 例如app/shop/[...slug]/page.js 会匹配 /shop/clothes、/shop/clothes/tops 和 /shop/clothes/tops/t-shirts 等等。 路由示例 URLparamsapp/shop/[...slug]/page.js/shop/a{ slug: [a] }app/shop/[...slug]/page.js/shop/a/b{ slug: [a, b] }app/shop/[...slug]/page.js/shop/a/b/c{ slug: [a, b, c] } 选择性捕获所有段 捕获所有段可以通过将参数包含在双方括号中而变得可选[[...folderName]]。 例如app/shop/[[...slug]]/page.js 会匹配 /shop、/shop/clothes、/shop/clothes/tops 和 /shop/clothes/tops/t-shirts。 捕获所有段和选择性捕获所有段的区别在于选择性也会匹配不带参数的路由。例如上述的 /shop 路由示例 URLparamsapp/shop/[[...slug]]/page.js/shop{}app/shop/[[...slug]]/page.js/shop/a{ slug: [a] }app/shop/[[...slug]]/page.js/shop/a/b{ slug: [a, b] }app/shop/[[...slug]]/page.js/shop/a/b/c{ slug: [a, b, c] } TypeScript 当使用 TypeScript 时你可以根据配置的路由段添加 params 的类型。 // app/blog/[slug]/page.tsxexport default function Page({ params }: { params: { slug: string } }) {return h1My Page/h1 }路由params 类型定义app/blog/[slug]/page.js{ slug: string }app/shop/[...slug]/page.js{ slug: string[] }app/[categoryId]/[itemId]/page.js{ categoryId: string, itemId: string } 需要知道这可能在将来由 TypeScript 插件自动完成 加载 UI 和流式传输 特殊的 loading.js 文件帮助你使用 React Suspense 创建有意义的 Loading UI。使用此约定你可以在加载路由段的内容时从服务器显示即使加载状态。初次渲染完成后新内容将自动交换。 即时加载状态 即时加载状态是导航时立即显示的回退 UI。你可以预渲染加载指示器例如骨架和旋转器或未来屏幕中一小部分但有意义的部分例如封面照片、标题等。这有助于用户了解应用程序的响应并提供更好的用户体验。 通过在文件夹添加 loading.js 文件来创建加载状态。 // app/dashboard/loading.tsxexport default function Loading() {// 可以在 Loading 中添加任何 UI包括骨架return LoadingSkeleton / }在同一个文件夹中loading.js 将嵌套在 layout.js 中。它会自动将 page.js 文件和下面的任何子级包装在 Suspense 边界中。 需要知道 即使是以服务器为中心的路由导航也是即时的导航是可中断的这意味着更改路由不需要等到路由内容完全加载后再导航到另一条路由加载新的路由段时共享布局保持交互式 建议将 loading.js 约定用于路由段布局和页面因为 Next.js 优化了此功能。 通过 Suspense 进行流式传输Streaming 除了 loading.js你还可为自己的 UI 组件手动创建 Suspense 范围。App Router 支持带有 Suspense 的流式传输用于 Node.js 和 Edge 运行时。 什么是流式传输 要了解流式传输是如何在 React 和 Next.js 中工作的首先理解服务端渲染SSR 及其局限性是很有帮助的。 使用 SSR在用户可以查看页面并与之交互之前需要完成一系列步骤 首先在服务器上获取给定页面的所有数据然后服务器为页面渲染 HTML页面的 HTML、CSS 和 JavaScript 被发送到客户端使用生成的 HTML 和 CSS 显示非交互式用户界面最后React 水合hydrates用户界面使其具有交互性 这些步骤是有顺序并且分块的意味着服务器只能在请求完所有数据后才能渲染 HTML。而且在客户端上React 只能在下载页面中所有组件的代码后对 UI 进行水合。 带有 React 和 Next.js 的 SSR 通过尽快向用户展示非交互式页面有助于提高感知加载性能。 然后它仍然可能很慢因为在向用户显示页面之前需要完成服务器上的所有数据提取。 流式传输允许你将 HTML 分解成更小的块并逐步将这些块从服务器发送到客户端。 这使得页面的部分内容能够更快地显示而无需等待所有数据加载后才能渲染 UI 界面。 流式传输与 React 的组件模型能很好的一起工作是因为每个组件可以视为一个块。具有更高优先级如产品信息或不依赖数据的组件可以先发送如布局并且 React 可以更早开始水合。优先级较低的组件如评论、相关产品可以在获取其数据后在同一服务器请求中发送。 当你希望防止长数据请求阻塞页面渲染时流式传输尤其有用因为它可以减少打开页面到获取第一个字节TTFB和打开页面到首个有意义绘制FCP的时间。并且它也有注入提高打开页面到可交互TTI的时间尤其是在速度较慢的设备上。 例子 Suspense 的工作原理是包装执行异步操作如获取数据的组件在执行异步操作时显示回退 UI如股价、微调器然后在操作完成后交换组件。 // app/dashboard/page.tsximport { Suspense } from react import { PostFeed, Weather } from ./Componentsexport default function Posts() {return (sectionSuspense fallback{pLoading feed.../p}PostFeed //SuspenseSuspense fallback{pLoading weather.../p}Weather //Suspense/section) }通过使用 Suspense你可以获得以下好处 流式传输服务器渲染 - 将 HTML 从服务器逐步渲染到客户端选择性水合 - React 根据用户交互优先考虑哪些组件可以进行交互 有关更多 Suspense 示例和用例请参阅 React 文档。 搜索引擎优化SEO Next.js 将等待 generateMetadata 内部的数据请求完成然后将 UI 流式传输到客户端。这保证了流式响应的第一部分包括 head 标签。 由于流式传输是服务端渲染的所以不会影响 SEO。你可以使用来自谷歌的工具 Mobile Friendly Test查看你的页面在谷歌网络爬虫上的显示方式并查看序列化的 HTML来源)。 状态码 当流式传输时将返回一个 200 状态码表示请求成功。 例如当使用 redirect 或 notFound 时服务器仍然可以在流式内容本身内向客户端传递错误或问题。由于响应头已经被送到了客户端因此无法修改响应的状态码。这不会影响 SEO。 错误处理 error.js 文件约定允许你优雅的处理嵌套路由中的意外运行时错误。 在 React 错误边界中自动包裹路由段及其嵌套子节点。使用文件系统层次结构来调整粒度创建针对特定段量身定制的错误 UI。将错误隔离到受影响的段同时保持应用程序的其余部分正常工作。添加功能尝试在不重新加载整页的情况下从错误中恢复。 通过在路由段内添加 error.js 文件并导出 React 组件来创建错误的 UI // app/dashboard/error.tsxuse client // 错误组件必须是客户端组件import { useEffect } from reactexport default function Error({error,reset, }: {error: Error { digest?: string }reset: () void }) {useEffect(() {console.error(error)}, [error])return (divh2Something went wrong!/h2buttononClick{// 尝试通过重新渲染来恢复() reset()}Try again/button/div) }error.js 的工作原理 error.js 会自动创建 React 错误边界它包装嵌套的子段或 page.js 组件从 error.js 文件导出的 React 组件用作回退组件如果在错误边界内抛出错误则回退组件被渲染时会包含该错误当回退错误组件处于活动状态时错误边界上级的布局将保持其状态和交互并且错误组件可以显示从错误中恢复的功能 从错误中恢复 错误的原因有时可能是暂时的。在这些情况下只需再次尝试就可以解决问题。 错误组件可以使用 reset() 函数来提示用户尝试从错误中恢复。当执行时该函数会尝试重新渲染报错的边界组件。如果成功则回退错误组件将替换为重新渲染的结果。 // app/dashboard/error.tsxuse clientexport default function Error({error,reset, }: {error: Error { digest?: string }reset: () void }) {return (divh2Something went wrong!/h2button onClick{() reset()}Try again/button/div) }嵌套路由 通过特殊文件创建的 React 组件将在特点的嵌套层次结构中进行渲染。 例如具有两个都包含 layout.js 和 error.js 文件的段的嵌套路由将在以下简化的组件层次结构中渲染 嵌套组件层次结构对 error.js 文件在嵌套路由中的行为有影响 错误冒泡到最近的父级错误边界。这意味着 error.js 文件将会处理其所有嵌套子段的错误。通过在路由的嵌套文件夹中放置不同级别的 error.js 文件可以实现或多或少的细粒度错误 UIerror.js 边界不会处理同一段中 layout.js 组件中抛出的错误因为错误边界嵌套在该布局的组件中 处理布局中的错误 error.js 边界不会捕获同一段中 layout.js 或者 template.js 组件中的抛出的错误。这种有意的层次结构使同级路由如导航栏之间共享的重要 UI 在发生错误时保持可见和可用。 要处理特点布局或模板中的错误请在布局的父级中添加 error.js 文件。 要处理根布局或模板中的错误请使用名为 global-error.js 的 error.js 变体。 在根布局中处理错误 根 app/error.js 边界不会捕获在根 app/layout.js 或 app/template.js 组件中排除的错误。 要专门处理这些根组件中的错误请使用位于根 app 目录中的名为 app/global-error.js 的 error.js 变体。 与根 app/error.js 不同的是global-error.js 错误边界封装了整个应用程序其回退组件在活动时会替换根布局。因此需要注意的是global-error.js 必须定义自己的 html 和 body 标签。 global-error.js 是粒度最小的错误 UI可以被视为整个应用程序的 “捕获所有” 错误处理。它不太可能经常被触发因为根组件通常不太动态通常不太动态其他 error.js 边界将会捕获大多数错误。 即使定义了 global-error.js仍然建议定义一个根 error.js其回退组件将在根布局中渲染其中包括全局共享的 UI 和品牌。 // app/global-error.tsxuse clientexport default function GlobalError({error,reset, }: {error: Error { digest?: string }reset: () void }) {return (htmlbodyh2Something went wrong!/h2button onClick{() reset()}Try again/button/body/html) }捕获服务器错误 如果在服务器组件内部引发错误Next.js 将把一个 Error 对象在生产中去掉了敏感的错误信息作为 error prop 转发到最近的 error.js 文件。 保护敏感错误信息 在生产过程中转发到客户端的 Error 对象只包括一个通用 message 和 digest 属性。 这是一种安全预防措施可避免将错误中包含的潜在敏感细节泄漏给客户端。 message 属性包含关于错误的通用信息digest 属性包含可用于匹配服务器端日志中相应错误的自动生成的错误哈希。 在开发过程中转发到客户端的 Error 对象将被序列化并包含原始错误的 message以便于调试。 并行路由 并行路由允许你同时或有条件地渲染同一布局中的一个或更多页面。对于应用的高度动态部分例如社交网站上的仪表盘和提要并行路由可用于实现复杂的路由模式。 例如你可以同时渲染团队和分析页面。 并行路由允许你为每个路由定义独立的错误和加载状态因为他们是独立流入的。 并行路由还允许你根据某些条件如身份验证状态有条件地渲染插槽。这将在同一 URL 上启用完全分离的代码。 约定 并行路由是通过命名插槽创建的。插槽使用 folder 约定定义的并且将会作为 props 被传递到同级的布局。 插槽不是路由段不会影响 URL 结构。文件路径 /team/members 可在 /members 中访问。 例如以下文件结构定义了两个显式插槽analytics 和 team。 上面的文件夹结构意味着 app/layout.js 中的组件现在接受 analytics 和 team 插槽 props并且可以与 children prop 并行渲染它们 // app/layout.tsxexport default function Layout(props: {children: React.ReactNodeanalytics: React.ReactNodeteam: React.ReactNode }) {return ({props.children}{props.team}{props.analytics}/) }需要知道children prop 是一个隐式插槽不需要映射到文件夹。这意味着 app/page.js 相当于 app/children/page.js。 不匹配的路由 默认情况下插槽中渲染的内容将与当前 URL 匹配。 在插槽不匹配的情况下Next.js 渲染的内容因路由技术和文件夹结构而异。 default.js 当 Next.js 无法根据当前 URL 恢复插槽的活动状态时你可以定义 default.js 文件来作为后备文件进行渲染。 请考虑以下文件夹结构。team 插槽有一个 settings 目录但是 analytics 没有。 导航 在导航时Next.js 将渲染插槽以前的活动状态即使它与当前 URL 不匹配。 重新加载 重新加载时Next.js 首先将尝试渲染不匹配插槽的 default.js 文件。如果不可用则会渲染 404。 不匹配路由的 404 有助于确保不会意外地渲染不应该并行渲染地路由。 useSelectedLayoutSegment(s) useSelectedLayoutSegment 和 useSelectedLayoutSegments 都接受 paralleRoutesKey它允许你读取该插槽中的活动路由段。 // app/layout.tsxuse clientimport { useSelectedLayoutSegment } from next/navigationexport default async function Layout(props: {//...auth: React.ReactNode }) {const loginSegments useSelectedLayoutSegment(auth)// ... }当用户导航到 URL 栏中的 auth/login 或 /login 时loginSegments 将等于字符串 login。 例子 模态框 并行路由可以用于渲染模态框。 auth 插槽渲染一个 Modal 组件该组件可以通过导航到匹配的路由例如/login来显示。 // app/layout.tsxexport default async function Layout(props: {// ...auth: React.ReactNode }) {return ({/* ... */}{props.auth}/) }// app/auth/login/page.tsximport { Modal } from components/modalexport default function Login() {return (Modalh1Login/h1{/* ... */}/Modal) }为了确保模态框的内容在它不活动时不会被渲染你可以创建 default.js 文件来返回 null。 // app/auth/default.tsxexport default function Default() {return null }关闭模态框 如果模态框是通过客户端导航启动的例如通过使用 Link href/login则可以通过调用 router.back() 或使用 Link 组件来关闭该模态框。 // app/auth/login/page.tsxuse client import { useRouter } from next/navigation import { Modal } from components/modalexport default async function Login() {const router useRouter()return (Modalspan onClick{() router.back()}Close modal/spanh1Login/h1/Modal) }有关模式的更多信息请参阅拦截路由部分。 如果你想要导航到其他地方并取消模态框你可以使用 “catch-all” 路由。 // app/auth/[...catchAll]/page.tsxexport default function CatchAll() {return null }“catch-all” 路由优先于 default.js。 条件路由 并行路由可用于实现条件路由。例如你可以根据身份验证状态渲染 dashboard 或 login 路由。 // app/layout.tsximport { getUser } from /lib/authexport default function Layout({dashboard,login, }: {dashboard: React.ReactNodelogin: React.ReactNode }) {const isLoggedIn getUser()return isLoggedIn ? dashboard : login }拦截路由 拦截路由允许你在当前布局中从应用程序的另一部分加载路由。当你希望显示路由的内容无需用户切换到不同上下文时这种路由规范可能很有用。 例如当点击提要中的图片时你可以在模态框中显示图片覆盖提要。在这种情况下Next.js 会拦截 /photo/123 路由屏蔽 URL并且将其覆盖在 /feed 上。 然而当通过点击可共享 URL 或刷新页面导航到图片时应该渲染整个图片而不是模态框。不应发生路由拦截。 约定 拦截路由可以用 (..) 约定来定义这与相对路径约定 ../ 类似区别在于拦截路由作用于片段。 你可以使用 (.) - 匹配同级片段(..) - 匹配上级片段(..)(..) - 匹配上上级片段(...) - 匹配 app 目录片段 例如你可以通过创建 (..)photo 目录从 feed 片段拦截 photo 片段。 注意(..) 约定基于路由段而不是文件系统。 例子 模态框 拦截路由可以与并行路由一起使用来创建模态框。 使用此模式来创建模态框克服了使用模态框时的一些常见挑战允许你 通过 URL 使模态框可共享刷新页面时保留上下文而不是关闭模态框通过回退路由关闭模态框而不是跳转上一个路由在前进路由时重新打开模态框 在上面的例子中photo 片段的路径可以使用 (..) 匹配器因为 modal 是一个插槽而非片段。这意味着 photo 路由只高出一个片段级别尽管它高出两个文件系统级别。 其他例子可能包括在顶部导航栏打开登录模态框同时还具有专用的 /login 页面或者在侧边栏中打开购物车。 查看示例具有拦截和平行路由的模式。 路由处理器 路由处理器允许你使用 Web Request 和 Response API 为给定路由创建自定义请求处理器。 需要知道路由处理器仅在 app 目录中可用。他们相当于 pages 目录中的 API 路由这意味着你不需要同时使用 API 路由和路由处理器。 约定 路由处理器在 app 目录的 route.js|ts 文件中定义。 // app/api/route.tsexport const dynamic force-dynamic // 默认 export async function GET(request: Request) {}路由处理器可以嵌套在 app 目录中与 page.js 和 layout.js 类似。但不能有与 page.js 文件相同路由段级别的 route.js 文件。 支持 HTTP 方法 支持以下的 HTTP 方法GET、POST、PUT、PATCH、DELETE、HEAD 和 OPTIONS。如果调用了不支持的方法Next.js 会返回 405 Method Not Allowed 响应。 扩展 NextRequest 和 NextResponse API 除了支持本地 Request 和 Response 外。Next.js 通过 NextRequest 和 NextResponse 来进行扩展为高级用例提供了方便的助手。 行为 缓存 路由处理器在使用 GET 方法来获取 Response 对象时默认会被缓存。 // app/items/route.tsexport async function GET() {const res await fetch(https://data.mongodb-api.com/..., {headers: {Content-Type: application/json,API-Key: process.env.DATA_API_KEY,},})const data await res.json()return Response.json({ data }) }TypeScript 警告Response.json() 仅在 TypeScript5.2 中有效。如果使用了更低版本的 TypeScript你可以对类型化响应使用 NextResponse.json()。 选择退出缓存 你可以选择退出缓存通过 将 Request 对象与 GET 方法一起使用使用其他任何 HTTP 方法使用动态函数如cookies 和 headers配置选项片段手动指定动态模式 例如 // app/products/api/route.tsexport async function GET(request: Request) {const { searchParams } new URL(request.url)const id searchParams.get(id)const res await fetch(https://data.mongodb-api.com/product/${id}, {headers: {Content-Type: application/json,API-Key: process.env.DATA_API_KEY,},})const product await res.json()return Response.json({ product }) }类似地POST 方法将导致对路由处理器进行动态评估。 // app/items/route.tsexport async function POST() {const res await fetch(https://data.mongodb-api.com/..., {method: POST,headers: {Content-Type: application/json,API-Key: process.env.DATA_API_KEY,},body: JSON.stringify({ time: new Date().toISOString() }),})const data await res.json()return Response.json(data) }需要知道像 API 路由一样路由处理器可以用于处理表单提交等情况。一种用于处理表单和突变的新的抽象概念正在开发中该抽象与 React 深度集成。 路由解决方案 可以将 route 视为最低级别的路由基元。 它们不参与布局或客户端导航与 page 不一样在与 page.js 相同的路由上不能有 route.js 文件 PageRouteResultapp/page.jsapp/route.js❌Conflictapp/page.jsapp/api/route.js✅Validapp/[user]/page.jsapp/api/route.js✅Valid 每个 route.js 或 page.js 文件都会接管该路由的所有 HTTP 行为。 // app/page.jsexport default function Page() {return h1Hello, Next.js!/h1 }// ❌ Conflict // app/route.js export async function POST(request) {}例子 以下示例展示了如何将 Route Handlers 与其他 Next.js API 和功能相结合。 重新验证缓存数据 你可以使用 next.revalidate 选项重新验证缓存数据 // app/items/route.tsexport async function GET() {const res await fetch(https://data.mongodb-api.com/..., {next: { revalidate: 60 }, // 每60分钟重新验证})const data await res.json()return Response.json(data) }或者你可以使用 revalidate 片段配置项 export const revalidate 60动态函数 路由处理器可以与 Next.js 中的动态函数一起使用如 cookies 和 handlers。 Cookies 你可以使用 next/headers 中的 cookies 读取 cookies。此服务器函数可以在路由处理器中直接调用也可以嵌套在另一个函数中。 此 cookies 实例是只读的。要设置 cookies你需要使用 Set-Cookie 头来返回一个新的 Response。 // app/api/route.tsimport { cookies } from next/headersexport async function GET(request: Request) {const cookieStore cookies()const token cookieStore.get(token)return new Response(Hello, Next.js!, {status: 200,headers: { Set-Cookie: token${token.value} },}) }或者你可以在底层 Web API 之上使用抽象来读取 cookieNextRequest // app/api/route.tsimport { type NextRequest } from next/serverexport async function GET(request: NextRequest) {const token request.cookies.get(token) }Headers 你可以从 next/headers 中的 headers 读取 headers。此服务器函数可以在路由处理器中直接调用或者嵌套在另一个函数中。 headers 实例是只读的。要设置 headers你需要返回一个带有 headers 的新响应。 // app/api/route.tsimport { headers } from next/headersexport async function GET(request: Request) {const headersList headers()const referer headersList.get(referer)return new Response(Hello, Next.js!, {status: 200,headers: { referer: referer },}) }或者你可以在底层 Web API 之上使用抽象来读取 headersNextRequest // app/api/route.tsimport { type NextRequest } from next/serverexport async function GET(request: NextRequest) {const requestHeaders new Headers(request.headers) }重定向 // app/api/route.tsimport { redirect } from next/navigationexport async function GET(request: Request) {redirect(https://nextjs.org/) }动态路由片段 路由处理器可以使用动态片段来根据动态数据创建请求处理器。 // app/items/[slug]/route.tsexport async function GET(request: Request,{ params }: { params: { slug: string } } ) {const slug params.slug // a, b, or c }RouteExample URLparamsapp/items/[slug]/route.js/items/a{ slug: a }app/items/[slug]/route.js/items/b{ slug: b }app/items/[slug]/route.js/items/c{ slug: c } URL 查询参数 传递给路由处理器的请求对象是一个 NextRequest 实例它有一些额外的方便方法包括更容易地处理查询参数。 // app/api/search/route.tsimport { type NextRequest } from next/serverexport function GET(request: NextRequest) {const searchParams request.nextUrl.searchParamsconst query searchParams.get(query)// 在 /api/search?queryhello 中 query 是 hello }流式传输 流式传输通常与大型语言模型LLM如OpenAI结合使用用于人工智能生产的内容。了解有关 AI SDK 的更多信息。 // app/api/chat/route.tsimport OpenAI from openai import { OpenAIStream, StreamingTextResponse } from aiconst openai new OpenAI({apiKey: process.env.OPENAI_API_KEY, })export const runtime edgeexport async function POST(req: Request) {const { messages } await req.json()const response await openai.chat.completions.create({model: gpt-3.5-turbo,stream: true,messages,})const stream OpenAIStream(response)return new StreamingTextResponse(stream) }这些抽象使用 Web API 来创建流你也可以直接使用底层的 Web API。 // app/api/route.ts// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream function iteratorToStream(iterator: any) {return new ReadableStream({async pull(controller) {const { value, done } await iterator.next()if (done) {controller.close()} else {controller.enqueue(value)}},}) }function sleep(time: number) {return new Promise((resolve) {setTimeout(resolve, time)}) }const encoder new TextEncoder()async function* makeIterator() {yield encoder.encode(pOne/p)await sleep(200)yield encoder.encode(pTwo/p)await sleep(200)yield encoder.encode(pThree/p) }export async function GET() {const iterator makeIterator()const stream iteratorToStream(iterator)return new Response(stream) }请求体 你可以通过使用标准化 Web API 方法来读取请求体 // app/items/route.tsexport async function POST(request: Request) {const res await request.json()return Response.json({ res }) }请求体表单数据 你可以通过 request.formData() 函数读取 FormData // app/items/route.tsexport async function POST(request: Request) {const formData await request.formData()const name formData.get(name)const email formData.get(email)return Response.json({ name, email }) }由于 formData 数据都是字符串你可能想要使用 zod-form-data 来验证请求和以你喜欢的格式如number检索数据。 CORS 你可以通过标准化 Web API 方法来设置响应的 CORS 头。 // app/api/route.tsexport const dynamic force-dynamic // defaults to force-staticexport async function GET(request: Request) {return new Response(Hello, Next.js!, {status: 200,headers: {Access-Control-Allow-Origin: *,Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS,Access-Control-Allow-Headers: Content-Type, Authorization,},}) }Edge 和 Node.js 运行环境 路由处理程序有一个同构的 Web API 来无缝支持 Edge 和 Node.js 运行环境包括对流式传输的支持。由于路由处理器使用与页面和布局相同的路由段配置因此它们支持期待已久的功能例如通用静态重新生成的路由处理器。 你可以使用 runtime 片段配置项来指定运行时 export const runtime edge // 默认是 nodejs非 UI 响应 你可以使用路由处理器来返回非 UI 内容。注意sitemap.xml、robots.txt、app icons 和打开的图形图像都有内置的支持。 // app/rss.xml/route.tsexport const dynamic force-dynamic // 默认是 force-staticexport async function GET() {return new Response(?xml version1.0 encodingUTF-8 ? rss version2.0channeltitleNext.js Documentation/titlelinkhttps://nextjs.org/docs/linkdescriptionThe React Framework for the Web/description /channel/rss) }片段配置项 路由处理器使用与页面和布局相同的路由段配置。 // app/items/route.tsexport const dynamic auto export const dynamicParams true export const revalidate false export const fetchCache auto export const runtime nodejs export const preferredRegion auto有关更多详细信息请参阅 API 参考资料。 中间件 中间件允许你在请求完成之前运行代码。然后根据传入的请求你可以通过重写、重定向、修改请求或响应头或直接响应来修改响应。 中间件在缓存内容和路由匹配之前运行。有关详细信息请参阅 Matching Paths。 约定 使用根目录中的 middleware.ts|js 文件来定义中间件。例如在 pages 或 app 同级或在 src 内部如果适用的话。 例子 // middleware.tsimport { NextResponse } from next/server import type { NextRequest } from next/server// 内部如果使用 await这个函数可以用 async 标记 export function middleware(request: NextRequest) {return NextResponse.redirect(new URL(/home, request.url)) }// 具体参考下方的“匹配路径” export const config {matcher: /about/:path*, }匹配路径 中间件将为项目中的每条路由调用。以下是执行顺序 next.config.js 中的 headersnext.config.js 中的 redirects中间件rewrites、redirects 等等next.config.js 中的 beforeFiles(rewrites)文件系统路由public/、_next/static/、pages/、app/ 等等next.config.js 中的 afterFiles(rewrites)动态路由/blog/[slug]next.config.js 中的 fallback(rewrites) 有两种方法可以定义中间件将在哪些路径上运行 自定义匹配器配置条件语句 匹配器 matcher 允许你过滤中间件以在特定路径上运行。 // middleware.jsexport const config {matcher: /about/:path*, }你可以使用数组语法匹配单个路径或多个路径 // middleware.jsexport const config {matcher: [/about/:path*, /dashboard/:path*], }matcher 配置允许使用完整的 regex因此支持像负前瞻性或字符匹配这样的匹配。此处可以看到一个负前瞻性示例用于匹配除特定路径之外的所有路径 // middleware.jsexport const config {matcher: [/** Match all request paths except for the ones starting with:* - api (API routes)* - _next/static (static files)* - _next/image (image optimization files)* - favicon.ico (favicon file)*//((?!api|_next/static|_next/image|favicon.ico).*),], }需要知道matcher 值必须是常量这样才能在构建时对其进行静态分析动态值如变量将被忽略 配置的匹配器 必须以 / 开头可以包括命名参数/about/:path 可以匹配 /about/a 和 /about/b但不能匹配 /about/b/c命名参数上可以有修饰符以 : 开头/about/:path* 匹配 /about/a/b/c因为 * 表示零或更多? 表示零或一 表示一或更多可以使用括号中的正则表达式/about/(.*) 与 /about/:path* 相同 需要知道为了相后兼容Next.js 总是将 /public 视为 /public/index因此/public 会被 /public/:path 的匹配器匹配。 条件语句 // middleware.tsimport { NextResponse } from next/server import type { NextRequest } from next/serverexport function middleware(request: NextRequest) {if (request.nextUrl.pathname.startsWith(/about)) {return NextResponse.rewrite(new URL(/about-2, request.url))}if (request.nextUrl.pathname.startsWith(/dashboard)) {return NextResponse.rewrite(new URL(/dashboard/user, request.url))} }NextResponse NextResponse API 允许你 redirect 传入的请求到其他 URL通过显示给定的 URL rewrite 响应设置 API 路由getServerSideProps 和 rewrite 目标的请求头设置响应 cookies设置响应 headers 要从中间件生成响应你可以 rewrite 到生成响应的路由页面或路由处理器。直接返回 NextResponse。请参阅生成响应。 使用 cookies Cookies 是常规头。在 Request 时它们存储在 Cookie 头中。在 Response 时它们存储在 Set-Cookie 头中。Next.js 提供了一种方便的方式来访问和操作这些 cookies即通过 NextRequest 和 NextResponse 上的 cookies 扩展。 对于传入请求cookies 具有以下方法get、getAll、set 和 delete cookies你可以使用 clear 检查是否存在 cookie或者使用删除所有 cookie。对于传出响应cookies 具有以下方法get、getAll、set 和 delete。 // middleware.tsimport { NextResponse } from next/server import type { NextRequest } from next/serverexport function middleware(request: NextRequest) {// 假设传入请求中存在 “Cookie:nextjsfast” 头// 使用 RequestCookies API 从请求中获取 cookielet cookie request.cookies.get(nextjs)console.log(cookie) // { name: nextjs, value: fast, Path: / }const allCookies request.cookies.getAll()console.log(allCookies) // [{ name: nextjs, value: fast }]request.cookies.has(nextjs) // truerequest.cookies.delete(nextjs)request.cookies.has(nextjs) // false// 使用 ResponseCookies API 在响应上设置 cookieconst response NextResponse.next()response.cookies.set(vercel, fast)response.cookies.set({name: vercel,value: fast,path: /,})cookie response.cookies.get(vercel)console.log(cookie) // { name: vercel, value: fast, Path: / }// 传出的响应将具有一个Set-Cookie:vercelfast;path/test 头return response }设置头 你可以使用 NextResponse API 设置请求和响应头从 Next.js v13.0.0 开始提供设置请求头。 // middleware.tsimport { NextResponse } from next/server import type { NextRequest } from next/serverexport function middleware(request: NextRequest) {// 克隆请求头并设置新请求头 x-hello-from-midleware1const requestHeaders new Headers(request.headers)requestHeaders.set(x-hello-from-middleware1, hello)// 你也可以在 NextResponse.write 中设置请求头const response NextResponse.next({request: {// 新的请求头headers: requestHeaders,},})// 设置新的响应头 x-hello-from-midleware2response.headers.set(x-hello-from-middleware2, hello)return response }需要知道避免设置大量的头因为这可能导致 431 Request Header Fields Too Large 错误取决于后端 web 服务器配置 产生响应 你可以通过返回 Response 或 NextResponse 实例直接从中间件进行响应。这从 Next.js v13.1.0 开始支持 // middleware.tsimport { NextRequest } from next/server import { isAuthenticated } from lib/auth// 限制为路径以 /api 开头的中间件 export const config {matcher: /api/:function*, }export function middleware(request: NextRequest) {// 调用我们的身份验证功能来检查请求if (!isAuthenticated(request)) {// 使用指示错误消息的 JSON 进行响应return Response.json({ success: false, message: authentication failed },{ status: 401 })} }高级中间件标志 在 Next.js v13.1 中为中间件引入了两个额外的标志skipMiddlewareUrlNormalize 和 skipTrailingSlashRedirect 以处理高级用例。 skipTrailingSlashRedirect 允许禁用 Next.js 默认重定向以添加或删除尾部斜杠从而允许在中间件内部进行自定义处理这可以允许为某些路径维护尾部斜杠但不允许为其他路径维护尾部斜线从而更容易地进行增量迁移。 // next.config.jsmodule.exports {skipTrailingSlashRedirect: true, }// middleware.jsconst legacyPrefixes [/docs, /blog]export default async function middleware(req) {const { pathname } req.nextUrlif (legacyPrefixes.some((prefix) pathname.startsWith(prefix))) {return NextResponse.next()}// 应用尾部斜线处理if (!pathname.endsWith(/) !pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]\/)*[^/]\.\w)/)) {req.nextUrl.pathname /return NextResponse.redirect(req.nextUrl)} }skipMiddlewareUrlNormalize 允许禁用 URL 规范化 Next.js 所做的操作以使处理直接访问和客户端转换相同。在一些高级情况下你需要使用解锁的原始 URL 进行完全控制。 // next.config.jsmodule.exports {skipMiddlewareUrlNormalize: true, }// middleware.jsexport default async function middleware(req) {const { pathname } req.nextUrl// GET /_next/data/build-id/hello.jsonconsole.log(pathname)// 带有 this now/next/data/build-id/hello.json 标志// 如果没有标志这将被规范化为 /hello }运行时 中间件目前仅支持 Edge 运行时。Node.js 运行时无法使用。 历史版本 VersionChangesv13.1.0添加高级中间件标志v13.0.0中间件可以修改请求头、响应头和发送响应v12.2.0中间件稳定请参阅升级指南v12.0.9在 Edge 运行时中强制执行绝对 URLPRv12.0.0添加中间件测试版 项目组织与文件托管 除了路由文件夹和文件约定外Next.js 对如何组织和并置项目文件没有任何偏见。 此页面共享默认行为和特点你可以用来组织你的项目 默认情况下安全主机代管项目组织功能项目组织策略 默认情况下安全主机代管 在 app 目录中嵌套文件夹层次结构定义了路由结构。 每个文件夹表示映射到 URL 路径中相应段的路由段。 然后即使路由结构是通过文件夹定义的在将 page.js 或 route.js 文件添加到路由段之前路由是不可公开访问的。 而且即使路由被公开访问也只有 page.js 或 route.js 返回的内容被发送到客户端。 这意味着项目文件可以在 app 目录中的路由段安全地内置而不会意外地变为可路由的。 需要知道 这与 pages 目录不同在 pages 目录中页面中的任何文件都被视为路由。虽然你可以在 app 中对你的项目文件进行并置但你不必这么做如果你愿意你可以将它们保存在 app 目录之外。 项目组织特点 Next.js 提供了几个功能来帮助你组织项目。 私有文件夹 可以通过在文件夹前加下划线来创建私有文件夹_folderName。 这表示文件夹是一个私有的实现细节路由系统不应考虑它从而选择跳出路由文件夹及其所有子文件夹。 由于默认情况下 app 目录中的文件可以安全地进行主机代管因此主机代管不需要私人文件夹。然而它们可以用于 将 UI 逻辑从路由逻辑中分离在项目和 Next.js 生态系统中持续组织内部文件在代码编辑器中对文件进行排序和分组避免与未来的 Next.js 文件约定发生潜在的命名冲突 需要知道 虽然不是框架约定但您也可以考虑使用相同的下划线模式将私有文件夹外的文件标记为 “private”你可以创建以下划线开头的 URL 段方法是在文件夹名称前加上 %5F下划线的 URL 编码形式%5FolderName如果你不使用私有文件夹了解 Next.js 的特殊文件约定会很有帮助以防止意外的命名冲突 路由组 路由组可以通过讲文件夹括在括号中来创建(folderNmae)。 这表示文件夹用于组织目的不因包含在路由的 URL 路径中。 路由组可用于 将路由分组例如按位置、意图或团队在同一的路由段级别中启用嵌套布局 在同一段创建多个嵌套布局包括多个根布局将布局添加到公共段的子路由中 src 目录 Next.js 支持将应用程序代码包括 app存储在可选的 src 目录中。这将应用程序代码与项目配置文件分离这些文件大多位于项目的根目录中。 模块路径别名 Next.js 支持模块路径别名使其更容易读取和维护深度嵌套项目文件中的导入。 // app/dashboard/settings/analytics/page.js// before import { Button } from ../../../components/button// after import { Button } from /components/button项目组织策略 在 Next.js 项目中组织自己的文件和文件夹时没有 “正确” 或 “错误” 的方法。 下一节列出了共同战略的高级概述。最简单的做法是选择一种适合你和你的团队策略并在整个项目中保持一致。 需要知道在下面的例子中我们使用 components 和 lib 文件夹作为通用占位符它们的命名没有特殊的框架意义您的项目可能会使用其他文件夹如 ui、utils、hooks、styles 等。 将项目文件存储在 app 外 此策略将所有应用程序代码存储在项目根目录中的共享文件夹中并保留应用程序目录纯粹用于路由目的。 将项目文件存储在 app 内部的顶级文件夹中 此策略将所有应用程序代码存储在 app 目录的根目录的共享文件夹中。 按特点或路由拆分项目文件 此策略将全局共享的应用程序代码存储在根 app 目录中并将更具体的应用程序编码拆分为使用它们的路由段。 国际化 Next.js 使你能够配置内容的路由和呈现以支持多种语言。使你的网站适应不同的地区包括翻译内容本地化和国际化路由。 专业术语 区域设置一组语言和格式首选项的标识符。这通常包括用户的首选语言以及可能的地理区域 en-US美式英语nl-NL荷兰式荷兰语nl荷兰语 路由概述 建议在浏览器中使用用户的语言首选项来选择要使用的区域设置。更改你的首选语言将修改应用程序中传入的 Accept-Language 头。 例如使用以下库你可以查看传入的 Request根据 Headers、计划支持的区域设置和默认区域设置来确定要选择的区域设置。 // middleware.jsimport { match } from formatjs/intl-localematcher import Negotiator from negotiatorlet headers { accept-language: en-US,en;q0.5 } let languages new Negotiator({ headers }).languages() let locales [en-US, nl-NL, nl] let defaultLocale en-USmatch(languages, locales, defaultLocale) // - en-US路由可以通过子路径/fr/productsa或域my-site.fr/products实现国际化。有了这些信息你现在可以根据中间件内部的区域设置重定向用户。 // middleware.jslet locales [en-US, nl-NL, nl]// 获取首选区域设置类似于上面的内容或使用库 function getLocale(request) { ... }export function middleware(request) {// 检查路径名中是否有任何支持的区域设置const { pathname } request.nextUrlconst pathnameHasLocale locales.some((locale) pathname.startsWith(/${locale}/) || pathname /${locale})if (pathnameHasLocale) return// 如果没有区域设置则重定向const locale getLocale(request)request.nextUrl.pathname /${locale}${pathname}// 例如传入的请求是 /product// 新的 URL 现在是 /en-US/productsreturn Response.redirect(request.nextUrl) }export const config {matcher: [// Skip all internal paths (_next)/((?!_next).*),// Optional: only run on root (/) URL// /], }最后确保 app/ 中的所有特殊文件都嵌套在 app/[lang] 下。这使 Next.js 路由器能够动态处理路由中的不同区域设置并将 lang 参数转发到每个布局和页面。例如 // app/[lang]/page.js// 你现在可以访问当前区域设置 // 例如/en-US/products - lang is en-US export default async function Page({ params: { lang } }) {return ... }根布局也可以嵌套在新文件夹中例如app/[lang]/layout.js。 本地化 根据用户的首选区域设置或本地化来更改显示的内容并不是 Next.js 特有的。下面描述的模式与任何 web 应用程序都一样。 假设我们希望在应用程序中同时支持英语和荷兰语内容。我们可能会维护两个不同的 “字典”它们是为我们提供从某个键到本地化字符串的映射的对象。例如 // dictionaries/en.json{products: {cart: Add to Cart} }// dictionaries/en.json{products: {cart: Toevoegen aan Winkelwagen} }然后我们可以创建一个 getDictionary 函数来加载所请求区域设置的翻译 // app/[lang]/dictionaries.jsimport server-onlyconst dictionaries {en: () import(./dictionaries/en.json).then((module) module.default),nl: () import(./dictionaries/nl.json).then((module) module.default), }export const getDictionary async (locale) dictionaries[locale]()给定当前选择的语言我们可以在布局或页面中获取字典。 // app/[lang]/page.jsimport { getDictionary } from ./dictionariesexport default async function Page({ params: { lang } }) {const dict await getDictionary(lang) // enreturn button{dict.products.cart}/button // Add to Cart }因为 app/ 目录中的所有布局和页面都默认为服务器组件所以我们不需要担心翻译文件的大小会影响客户端 JavaScript 分包的大小。此代码将仅在服务器上运行并且只有生成的 HTML 才会发送到浏览器。 静态生成 要为给定的一组区域设置生成静态路由我们可以对任何页面或布局使用 generateStaticParams。这可以是全局的例如在根布局中 // app/[lang]/layout.jsexport async function generateStaticParams() {return [{ lang: en-US }, { lang: de }] }export default function Root({ children, params }) {return (html lang{params.lang}body{children}/body/html) }资源 Minimal i18n routing and translationsnext-intlnext-internationalnext-i18n-router
http://www.pierceye.com/news/435098/

相关文章:

  • 网站同时做竞价和优化可以吗做网站游戏推广赚钱吗
  • 台州建站模板搭建上海远程教育网站设计与开发公司
  • 网站如何做淘客类似58同城分类信息网站开发
  • 网站源码文件安装教程苏州网站建设致宇
  • 免费注册域名网站知乎做网站做图电脑需要什么配置
  • 高埗做网站营销策略分析包括哪些内容
  • wordpress获取站点链接网站门户
  • flashxml网站模板网站后期培训机构全国排名
  • 企业网站设计网站页面设计中为什么要有优先级排列
  • 暗网是什么网站滨江区网站开发公司
  • 南京网站排名优化费用株洲58同城网站建设电话
  • 电子商务网站建设与管理理解上海网站推广企业
  • 设计师网站pintsetseo短视频网页入口引流免费
  • 个人如何注册微信公众号怎么创建网站优化的意义
  • 网站换空间要重新备案吗百度人工电话
  • 做网站要注意哪些问题网站用什么工具做
  • 在福州的网站制作公司滨海新网站建设
  • 帝国网站地图插件泰兴企业网站建设
  • wordpress布置网站教程用dw做简单图片网站
  • 网页制作模板左右结构百度seo关键词优化方案
  • 长沙设备建站按效果付费wordpress可视化编辑器插件
  • 软件开发与网站开发硬件开发语言
  • 开封做网站睿艺美官方网站建设的必要
  • 自适应网站制作简创网络南联网站建设
  • 帮别人做钓鱼网站犯法吗贵州网站建设工作室
  • 企业网站域名空间优化公司治理结构
  • 网站建设 前沿文章php做网站脑图
  • 刷单网站开发装修企业网站源码
  • 莱州人社局网站网站开发项目资金运用明细
  • 水墨网站模板软通动力外包怎么样