一看就会的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

目录
相关文章
|
15小时前
uni-app 65egg.js聊天类chat.js封装(二)
uni-app 65egg.js聊天类chat.js封装(二)
29 1
|
16小时前
|
JavaScript
uni-app中关于格式化时间的js文件
uni-app中关于格式化时间的js文件
37 0
|
16小时前
|
开发框架 移动开发 JavaScript
uni-app 68 egg.js发送消息接口开发-单聊(一)
uni-app 68 egg.js发送消息接口开发-单聊(一)
|
16小时前
|
移动开发 JavaScript 前端开发
uni-app和Vue.js二者之间有什么区别?
1. uni-app是一个使用Vue.js开发所有前端应用的框架,支持一次编译多端运行。开发者编写的基础代码只需进行一次编写,就可以发布到多个平台,包括App、H5、微信小程序等。 2. Vue.js是一个渐进式JavaScript框架,用于构建用户界面。与其他大型框架不同的是,Vue被设计为可以自底向上逐层应用。
5 0
|
16小时前
uni-app 68 egg.js发送消息接口开发-单聊(一)
uni-app 68 egg.js发送消息接口开发-单聊(一)
27 7
|
15小时前
uni-app 66聊天类chat.js封装(三)
uni-app 66聊天类chat.js封装(三)
33 6
|
15小时前
uni-app 64聊天类chat.js封装(一)
uni-app 64聊天类chat.js封装(一)
28 2
|
15小时前
uni-app 63egg.js后端用户上线和下线深度剖析
uni-app 63egg.js后端用户上线和下线深度剖析
16 1
|
15小时前
|
中间件 API 数据库
uni-app 24egg.js 基础课
uni-app 24egg.js 基础课
31 0
|
16小时前
|
开发框架 JavaScript 前端开发
深入探讨Vue.js核心技术及uni-app跨平台开发实践
深入探讨Vue.js核心技术及uni-app跨平台开发实践
59 0