一看就会的Next.js App Router版 -- Routing(下)(三)

简介: 一看就会的Next.js App Router版 -- Routing

Example

例子

middleware.ts

python

import { NextResponse } from 'next/server';import type { NextRequest } from 'next/server'; // This function can be marked `async` if using `await` insideexport function middleware(request: NextRequest) {  return NextResponse.redirect(new URL('/home', request.url));} // See "Matching Paths" below to learn moreexport const config = {  matcher: '/about/:path*',};
Matching Paths

匹配路径

Middleware will be invoked for every route in your project. The following is the execution order:

  1. headers from next.config.js
  2. 来自 next.config.js 的标头
  3. redirects from next.config.js
  4. 从 next.config.js 重定向
  5. Middleware (rewrites, redirects, etc.)
  6. 中间件(重写、重定向等)
  7. beforeFiles (rewrites) from next.config.js
  8. Filesystem routes (public/, _next/static/, pages/, app/, etc.)
  9. 文件系统路由(public/、_next/static/、pages/、app/ 等)
  10. afterFiles (rewrites) from next.config.js
  11. 来自 next.config.js 的 afterFiles(重写)
  12. Dynamic Routes (/blog/[slug])
  13. 动态路由 (/blog/[slug])
  14. fallback (rewrites) from next.config.js

There are two ways to define which paths Middleware will run on:

有两种方法可以定义中间件将在哪些路径上运行:

  1. Custom matcher config
  2. 自定义匹配器配置
  3. Conditional statements
  4. 条件语句

Matcher

火柴

matcher allows you to filter Middleware to run on specific paths.

匹配器允许您过滤中间件以在特定路径上运行。

middleware.js

arduino

export const config = {  matcher: '/about/:path*',};

You can match a single path or multiple paths with an array syntax:

您可以使用数组语法匹配单个路径或多个路径:

middleware.js

arduino

export const config = {  matcher: ['/about/:path*', '/dashboard/:path*'],};

The matcher config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here:

匹配器配置允许完整的正则表达式,因此支持否定前瞻或字符匹配等匹配。可以在此处看到匹配除特定路径之外的所有路径的否定前瞻示例:

middleware.js

arduino

export 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).*)',  ],};
Note: The matcher values need to be constants so they can be statically analyzed at

复制代码

build-time. Dynamic values such as variables will be ignored.

注意:匹配器值需要是常量,以便可以在构建时对其进行静态分析。变量等动态值将被忽略。

Configured matchers:

配置的匹配器:

  1. MUST start with /
  2. 必须以 / 开头
  3. Can include named parameters: /about/:path matches /about/a and /about/b but not /about/a/c
  4. 可以包含命名参数:/about/:path 匹配 /about/a 和 /about/b 但不匹配 /about/a/c
  5. Can have modifiers on named parameters (starting with :): /about/:path* matches /about/a/b/c because * is zero or more. ? is zero or one and +one or more
  6. 可以在命名参数上使用修饰符(以 : 开头):/about/:path* 匹配 /about/a/b/c 因为 * 为零或更多。 ?是零或一和一或更多
  7. Can use regular expression enclosed in parenthesis: /about/(.*) is the same as /about/:path*
  8. 可以使用括号括起来的正则表达式:/about/(.) 等同于/about/:path

Read more details on path-to-regexp documentation.

阅读有关正则表达式路径的更多详细信息

文档。

Note: For backward compatibility, Next.js always considers /public as /public/index. Therefore, a matcher of /public/:path will match.

注意:为了向后兼容,Next.js 始终将 /public 视为 /public/index。因此, /public/:path 的匹配器将匹配。

Conditional Statements

middleware.ts

import { NextResponse } from 'next/server';import type { NextRequest } from 'next/server'; export 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

下一个响应

The NextResponse API allows you to:

  • redirect the incoming request to a different URL
  • 将传入请求重定向到不同的 URL
  • rewrite the response by displaying a given URL
  • 通过显示给定的 URL 重写响应
  • Set request headers for API Routes, getServerSideProps, and rewrite destinations
  • 为 API 路由、getServerSideProps 和重写目标设置请求标头
  • Set response cookies
  • Set response headers
  • 设置响应头

To produce a response from Middleware, you can:

要从中间件产生响应,您可以:

  1. rewrite to a route (Page or Edge API Route) that produces a response
  2. 重写为产生响应的路由(页面或边缘 API 路由)
  3. return a NextResponse directly. See Producing a Response

Using Cookies

Cookies are regular headers. On a Request, they are stored in the Cookie header. On a Response they are in the Set-Cookie header. Next.js provides a convenient way to access and manipulate these cookies through the cookies extension on NextRequest and NextResponse.

Cookie 是常规标头。在请求中,它们存储在 Cookie 标头中。在响应中,它们位于 Set-Cookie 标头中。 Next.js 通过 NextRequest 和 NextResponse 上的 cookie 扩展提供了一种访问和操作这些 cookie 的便捷方式。

  1. For incoming requests, cookies comes with the following methods: get, getAll, set, and delete cookies. You can check for the existence of a cookie with has or remove all cookies with clear.
  2. 对于传入请求,cookie 带有以下方法:get、getAll、set 和 delete cookie。您可以使用 has 检查 cookie 是否存在或使用 clear 删除所有 cookie。
  3. For outgoing responses, cookies have the following methods get, getAll, set, and delete.
  4. 对于传出响应,cookie 具有以下方法 get、getAll、set 和 delete。

middleware.ts

sql

复制代码

import { NextResponse } from 'next/server';import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) {  // Assume a "Cookie:nextjs=fast" header to be present on the incoming request  // Getting cookies from the request using the `RequestCookies` API  let cookie = request.cookies.get('nextjs')?.value;  console.log(cookie); // => 'fast'  const allCookies = request.cookies.getAll();  console.log(allCookies); // => [{ name: 'nextjs', value: 'fast' }]   request.cookies.has('nextjs'); // => true  request.cookies.delete('nextjs');  request.cookies.has('nextjs'); // => false   // Setting cookies on the response using the `ResponseCookies` API  const response = NextResponse.next();  response.cookies.set('vercel', 'fast');  response.cookies.set({    name: 'vercel',    value: 'fast',    path: '/test',  });  cookie = response.cookies.get('vercel');  console.log(cookie); // => { name: 'vercel', value: 'fast', Path: '/test' }  // The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.   return response;}
Setting Headers

设置标题

You can set request and response headers using the NextResponse API (setting request headers is available since Next.js v13.0.0).

middleware.ts

TypeScript

python

import { NextResponse } from 'next/server';import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) {  // Clone the request headers and set a new header `x-hello-from-middleware1`  const requestHeaders = new Headers(request.headers);  requestHeaders.set('x-hello-from-middleware1', 'hello');   // You can also set request headers in NextResponse.rewrite  const response = NextResponse.next({    request: {      // New request headers      headers: requestHeaders,    },  });   // Set a new response header `x-hello-from-middleware2`  response.headers.set('x-hello-from-middleware2', 'hello');  return response;}
Note: Avoid setting large headers as it might cause 431 Request Header Fields Too

Large error depending on your backend web server configuration.

Producing a Response

You can respond from Middleware directly by returning a Response or NextResponse instance. (This is available since Next.js v13.1.0)

您可以通过返回 Response 或 NextResponse 实例直接从中间件响应。 (自 Next.js v13.1.0 起可用

)

middleware.ts

TypeScript

sql

import { NextRequest, NextResponse } from 'next/server';import { isAuthenticated } from '@lib/auth'; // Limit the middleware to paths starting with `/api/`export const config = {  matcher: '/api/:function*',}; export function middleware(request: NextRequest) {  // Call our authentication function to check the request  if (!isAuthenticated(request)) {    // Respond with JSON indicating an error message    return new NextResponse(      JSON.stringify({ success: false, message: 'authentication failed' }),      { status: 401, headers: { 'content-type': 'application/json' } },    );  }}
Advanced Middleware Flags

高级中间件标志

In v13.1 of Next.js two additional flags were introduced for middleware, skipMiddlewareUrlNormalize and skipTrailingSlashRedirect to handle advanced use cases.

skipTrailingSlashRedirect allows disabling Next.js default redirects for adding or removing trailing slashes allowing custom handling inside middleware which can allow maintaining the trailing slash for some paths but not others allowing easier incremental migrations.

skipTrailingSlashRedirect 允许禁用 Next.js 默认重定向以添加或删除尾部斜杠,从而允许在中间件内部进行自定义处理,这可以允许为某些路径保留尾部斜杠,而不是其他路径,从而允许更轻松的增量迁移。

next.config.js

ini

module.exports = {  skipTrailingSlashRedirect: true,};
middleware.js
const legacyPrefixes = ['/docs', '/blog']; export default async function middleware(req) {  const { pathname } = req.nextUrl;   if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {    return NextResponse.next();  }   // apply trailing slash handling  if (    !pathname.endsWith('/') &&    !pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)  ) {    req.nextUrl.pathname += '/';    return NextResponse.redirect(req.nextUrl);  }}
skipMiddlewareUrlNormalize allows disabling the URL normalizing Next.js does to

make handling direct visits and client-transitions the same. There are some advanced cases where you need full control using the original URL which this unlocks.

skipMiddlewareUrlNormalize 允许禁用 URL 规范化 Next.js 确实可以处理直接访问和客户端转换。在某些高级情况下,您需要使用此解锁的原始 URL 进行完全控制。

next.config.js

ini

module.exports = {  skipMiddlewareUrlNormalize: true,};

middleware.js

export default async function middleware(req) {  const { pathname } = req.nextUrl;   // GET /_next/data/build-id/hello.json   console.log(pathname);  // with the flag this now /_next/data/build-id/hello.json  // without the flag this would be normalized to /hello}
Internationalization

国际化

Next.js enables you to configure the routing and rendering of content to support multiple languages. Making your site adaptive to different locales includes translated content (localization) and internationalized routes.

Next.js 使您能够配置内容的路由和呈现以支持多种语言。使您的站点适应不同的区域设置包括翻译内容(本地化)和国际化路线。

Terminology

术语

  • Locale: An identifier for a set of language and formatting preferences. This usually includes the preferred language of the user and possibly their geographic region.
  • en-US: English as spoken in the United States
  • en-US:在美国使用的英语
  • nl-NL: Dutch as spoken in the Netherlands
  • nl-NL:荷兰语中的荷兰语
  • nl: Dutch, no specific region
  • nl:荷兰语,无特定区域
  • 区域设置:一组语言和格式首选项的标识符。这通常包括用户的首选语言以及可能的地理区域。
    en-US:在美国使用的英语
    nl-NL:荷兰语中的荷兰语
    nl:荷兰语,无特定区域

Routing Overview

路由概述

It’s recommended to use the user’s language preferences in the browser to select which locale to use. Changing your preferred language will modify the incoming Accept-Language header to your application.

建议在浏览器中使用用户的语言首选项来选择要使用的语言环境。更改您的首选语言将修改传入的 Accept-Language 标头到您的应用程序。

For example, using the following libraries, you can look at an incoming Request to determine which locale to select, based on the Headers, locales you plan to support, and the default locale.

例如,使用以下库,您可以根据标头、您计划支持的区域设置和默认区域设置,查看传入请求以确定选择哪个区域设置。

middleware.js

javascript

复制代码

import { match } from '@formatjs/intl-localematcher';import Negotiator from 'negotiator'; let headers = { 'Accept-Language': 'en-US,en;q=0.5' };let languages = new Negotiator(headers).languages();let locales = ['en-US', 'nl-NL', 'nl'];let defaultLocale = 'en-US'; match(languages, locales, defaultLocale); // -> 'en-US'

Routing can be internationalized by either the sub-path (/fr/products) or domain (my-site.fr/products). With this information, you can now redirect the user based on the locale inside Middleware.

路由可以通过子路径 (/fr/products) 或域 (my-site.fr/products) 国际化。有了这些信息,您现在可以根据 Middleware 中的语言环境重定向用户。

middleware.js

sql

import { NextResponse } from 'next/server' let locales = ['en-US', 'nl-NL', 'nl'] // Get the preferred locale, similar to above or using a libraryfunction getLocale(request) { ... } export function middleware(request) {  // Check if there is any supported locale in the pathname  const pathname = request.nextUrl.pathname  const pathnameIsMissingLocale = locales.every(    (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`  )   // Redirect if there is no locale  if (pathnameIsMissingLocale) {    const locale = getLocale(request)     // e.g. incoming request is /products    // The new URL is now /en-US/products    return NextResponse.redirect(      new URL(`/${locale}/${pathname}`, request.url)    )  }} export const config = {  matcher: [    // Skip all internal paths (_next)    '/((?!_next).*)',    // Optional: only run on root (/) URL    // '/'  ],}

Finally, ensure all special files inside app/ are nested under app/[lang]. This enables the Next.js router to dynamically handle different locales in the route, and forward the lang parameter to every layout and page. For example:

最后,确保 app/ 中的所有特殊文件都嵌套在 app/[lang] 下。这使 Next.js 路由器能够动态处理路由中的不同语言环境,并将 lang 参数转发到每个布局和页面。例如:

app/[lang]/page.js

vbnet

// You now have access to the current locale// e.g. /en-US/products -> `lang` is "en-US"export default async function Page({ params: { lang } }) {  return ...}

The root layout can also be nested in the new folder (e.g. app/[lang]/layout.js).

根布局也可以嵌套在新文件夹中(例如 app/[lang]/layout.js)。

Localization

本土化

Changing displayed content based on the user’s preferred locale, or localization, is not something specific to Next.js. The patterns described below would work the same with any web application.

根据用户的首选语言环境或本地化更改显示的内容并不是 Next.js 特有的。下面描述的模式适用于任何 Web 应用程序。

Let’s assume we want to support both English and Dutch content inside our application. We might maintain two different “dictionaries”, which are objects that give us a mapping from some key to a localized string. For example:

假设我们希望在我们的应用程序中同时支持英语和荷兰语内容。我们可能会维护两个不同的“字典”,它们是为我们提供从某个键到本地化字符串的映射的对象。例如:

dictionaries/en.json

{  "products": {    "cart": "Add to Cart"  }}

dictionaries/nl.json

{  "products": {    "cart": "Toevoegen aan Winkelwagen"  }}

We can then create a getDictionary function to load the translations for the requested locale:

然后我们可以创建一个 getDictionary 函数来加载所请求语言环境的翻译:

app/[lang]/dictionaries.js

import 'server-only'; const 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]();

Given the currently selected language, we can fetch the dictionary inside of a layout or page.

给定当前选择的语言,我们可以在布局或页面中获取字典。

app/[lang]/page.j

import { getDictionary } from './dictionaries'; export default async function Page({ params: { lang } }) {  const dict = await getDictionary(lang); // en  return <button>{dict.products.cart}</button>; // Add to Cart}

Because all layouts and pages in the app/ directory default to Server Components, we do not need to worry about the size of the translation files affecting our client-side JavaScript bundle size. This code will only run on the server, and only the resulting HTML will be sent to the browser.

因为app/目录下的所有布局和页面默认都是Server Components  ,我们无需担心翻译文件的大小会影响我们客户端 JavaScript 包的大小。此代码只会在服务器上运行,并且只会将生成的 HTML 发送到浏览器。

Static Generation

静态生成

To generate static routes for a given set of locales, we can use generateStaticParams with any page or layout. This can be global, for example, in the root layout:

要为一组给定的语言环境生成静态路由,我们可以对任何页面或布局使用 generateStaticParams。这可以是全局的,例如,在根布局中:

app/[lang]/layout.js

export async function generateStaticParams() {  return [{ lang: 'en-US' }, { lang: 'de' }];} export default function Root({ children, params }) {  return (    <html lang={params.lang}>      <body>{children}</body>    </html>  );}

Examples

目录
相关文章
|
1月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
547 1
|
2月前
|
监控 JavaScript 前端开发
深入理解 Nuxt.js 中的 app:error 钩子
【9月更文挑战第25天】在 Nuxt.js 中,`app:error` 钩子是一个强大的工具,用于处理应用程序中的各种错误。它可以在服务器端渲染或客户端运行时触发,帮助提升应用稳定性和用户体验。通过在 `nuxt.config.js` 中定义该钩子,开发者可以实现错误页面显示、错误日志记录及错误恢复等功能,确保应用在遇到问题时能妥善处理并恢复正常运行。
49 10
|
3月前
|
资源调度 JavaScript Linux
【Azure 应用服务】本地Node.js部署上云(Azure App Service for Linux)遇到的三个问题解决之道
【Azure 应用服务】本地Node.js部署上云(Azure App Service for Linux)遇到的三个问题解决之道
|
2月前
|
开发者 UED
深入理解 Nuxt.js 中的 app:error 钩子
【9月更文挑战第26天】在 Nuxt.js 中,钩子函数是在特定生命周期阶段执行代码的机制,`app:error` 钩子用于处理应用中的错误,包括服务器端和客户端渲染时出现的问题。它提供了一个集中处理错误的机制,提升了用户体验。当组件渲染过程中出现错误时,`app:error` 钩子会被触发,可以在 `nuxt.config.js` 文件中定义该钩子。通过分析错误对象 `err` 和上下文信息 `context`,开发者可以更好地处理各种错误情况。相比组件内的 `try/catch` 或浏览器原生错误处理,`app:error` 提供了更全局和有针对性的错误处理方式。
|
3月前
|
JavaScript 前端开发
【Azure Developer】在App Service上放置一个JS页面并引用msal.min.js成功获取AAD用户名示例
【Azure Developer】在App Service上放置一个JS页面并引用msal.min.js成功获取AAD用户名示例
|
3月前
|
JavaScript 前端开发 UED
揭秘Vue.js高效开发:Vue Router如何让单页面应用路由管理变得如此简单?
【8月更文挑战第30天】随着Web应用复杂性的增加,单页面应用(SPA)因出色的用户体验和高效的页面加载性能而备受青睐。Vue.js凭借简洁的语法和灵活的组件系统成为构建SPA的热门选择,其官方路由管理器Vue Router则简化了路由管理。本文通过实战示例介绍如何利用Vue Router实现高效的SPA路由管理,包括命名路由、动态路由及其核心优势。
36 0
|
3月前
|
JavaScript Windows
【Azure 应用服务】用App Service部署运行 Vue.js 编写的项目,应该怎么部署运行呢?
【Azure 应用服务】用App Service部署运行 Vue.js 编写的项目,应该怎么部署运行呢?
|
3月前
|
开发框架 JavaScript 前端开发
【Azure Developer】App Service + PubSub +JS 实现多人版黑客帝国文字流效果图
【Azure Developer】App Service + PubSub +JS 实现多人版黑客帝国文字流效果图
|
3月前
|
前端开发 JavaScript Linux
【Azure 应用服务】在Azure App Service for Linux环境中,部署的Django应用,出现加载css、js等静态资源文件失败
【Azure 应用服务】在Azure App Service for Linux环境中,部署的Django应用,出现加载css、js等静态资源文件失败
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的成人教育APP附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的成人教育APP附带文章源码部署视频讲解等
57 2