每个人都应该知道的 28 个 NextJS 高级功能

这是关于 NextJS 一些鲜为人知的功能的指南。

我在本文中包含了我在我开发的 NextJS 应用程序中未见过的内容。 也许这些功能比我意识到的更受欢迎,但无论如何,希望您遇到一些对您来说是新的功能。 如果您有建议,请发表评论。

本文假设您熟悉 NextJS(页面和应用程序路由器),我不会在这里讨论任何基本/典型功能


您可以防止您的拼写错误 组件通过打开 typedRoutes 配置选项。

这将生成一个 .d.ts 类型文件在 .next/types,其中包含所有路线的输入信息,以便 Typescript 可以检查 href 值是有效的。

next.config.js

✅ copied

const nextConfig = { experimental: { typedRoutes: true, }, } module.exports = nextConfig

有一些警告 – 最重要的是非文字字符串(例如动态创建的字符串,例如 /blog/${slug} 不会起作用,你必须施放它 as Route)。

他们的文档以获取完整信息


一些常见的 Google 第三方脚本的官方 NextJS 包

有一些使用 Google 第三方脚本的官方软件包。

所有官方第三方脚本均来自 Google – 并且可以通过添加 @next/third-parties/google 依赖性。

  • GoogleTagManager 将 Google 跟踪代码管理器容器实例化到您的页面
  • GoogleAnalytics 设置 Google Analytics(在他们的文档中,但尚未在 npm 包中)
  • GoogleMapsEmbed 延迟加载 Google 地图实例
  • YouTubeEmbed 快速加载 Youtube 嵌入内容。 它使用 精简版 YouTube 嵌入 包裹。

用法示例:

app/layout.tsx

✅ copied

import { YouTubeEmbed } from '@next/third-parties/google' export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body>{children}body> <YouTubeEmbed videoid="ogfYd705cRs" height={400} params="controls=0" /> html> ) }


NextJS 关于将 .env 放入存储库的建议

我对这个建议感到惊讶,尽管我可以理解他们的理由。 我有 绝不 见过 .env 致力于 git。 有时 .env.example (虚拟值,您复制到 .env) 或者 .env.test (用于 CI 或在本地运行测试)。

这是他们文档的直接引用:

很高兴知道:.env、.env.development 和 .env.Production 文件应包含在您的存储库中,因为它们定义了默认值。 .env*.local 应添加到 .gitignore,因为这些文件将被忽略。 .env.local 是可以存储机密的地方。


轻松生成 sitemap.xml

如果你放一个 sitemap.ts 文件在你的 app 目录,您可以使用它生成文件数组以包含在 sitemap.xml 文件。

下面的示例显示了硬编码的站点地图项,但您可以将此函数转换为异步函数并从 API 或数据库动态加载。

app/sitemap.ts

✅ copied

import { MetadataRoute } from 'next' export default function sitemap(): MetadataRoute.Sitemap { return [ { url: 'https://acme.com', lastModified: new Date(), changeFrequency: 'yearly', priority: 1, }, { url: 'https://acme.com/blog', lastModified: new Date(), changeFrequency: 'weekly', priority: 0.5, }, ] }

了解更多 查看他们的文档


不稳定_缓存(应用程序路由器)

您可以使用 unstable_cache 缓存数据(例如数据库数据)并在多个查询之间使用缓存的数据。

顾名思义,这是不稳定的。 但根据我的经验,它的工作原理正如你所期望的那样。 NextJS 的未来版本有可能会改变它的工作方式(以及名称!)

someComponent.tsx

✅ copied

import { getUser } from './data'; import { unstable_cache } from 'next/cache'; const getCachedUser = unstable_cache( async (id) => getUser(id), ['my-app-user'] ); export default async function Component({ userID }) { const user = await getCachedUser(userID); ... }


通过将 CI 配置为使用 .next/cache 来改进 CI 的构建缓存

接下来将使用 .next/cache 目录来保存一些可以在不同版本之间使用的缓存数据。

要在 CI 提供程序中使用此缓存,通常必须手动配置它。

这是通过 Vercel 自动完成的

这是一个例子 Github 操作,但是其他提供商的文档中有更多示例,例如 CircleCI、GitLab CI、BitBucket Pipelines 等。

build-workflow.yml

✅ copied

uses: actions/cache@v3 with: path: | ~/.npm ${{ github.workspace }}/.next/cache key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} restore-keys: | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-

查看 CI 文档在这里 获取完整信息。


获取用户的地理位置(如果您使用 Vercel)

接下来将使用 NextRequest 传递请求对象时的对象。 如果您使用过路由处理程序,您可能会熟悉它:

export async function GET(request) { // request param is the NextRequest return Response.json('some-response') }

在 Vercel 上,您会自动将地理位置添加到请求中。

request.geo.city request.geo.country request.geo.region request.geo.latitude request.geo.longitude

如果您使用 Vercel 进行部署,则可以使用以下命令访问 IP request.ip, 或者 request.headers.get('X-Forwarded-For') 在大多数其他服务器上。


轻松删除 JSX 属性(例如 data-test

您可以轻松删除 JSX 属性 babel-plugin-react-remove-properties。 您可能希望使用以下属性来执行此操作 data-testid="SomeTestId"

如果您使用 Babel,那么设置起来很容易。

next.config.js

✅ copied

module.exports = { compiler: { reactRemoveProperties: true, // by default it will remove data-testid // but you can set `properties` to define a regex }, }


Next 的动态加载器

NextJS 有 next/dynamic,这是两者的组合 React.lazy() (让您推迟加载组件的代码,直到第一次渲染为止)和 (让您显示回退,直到其子级完成加载)。

以下是如何使用它的示例:

'use client' import { useState } from 'react' import dynamic from 'next/dynamic' // Client Components: const ComponentA = dynamic(() => import('../components/A')) const ComponentB = dynamic(() => import('../components/B')) const ComponentC = dynamic(() => import('../components/C'), { ssr: false }) export default function ClientComponentExample() { const [showMore, setShowMore] = useState(false) return ( <div> {/* Load immediately, but in a separate client bundle */} <ComponentA /> {/* Load on demand, only when/if the condition is met */} {showMore && <ComponentB />} <button onClick={() => setShowMore(!showMore)}>Togglebutton> {/* Load only on the client side */} <ComponentC /> div> ) }

一个相关的功能是您还可以根据需要加载其他脚本/文件。 这是一个例子:

export default function Page() { return ( <button onClick={async () => { const Fuse = (await import('fuse.js')).default const fuse = new Fuse(someData) console.log(fuse.search(value)) }} /> ) }

了解快速刷新(热重载)的工作原理

快速刷新是指在编辑文件时几乎立即重新加载组件(不会丢失状态)。

文档 更详细地介绍它,但我想指出一些有关快速刷新的重要事项。

  • “如果您编辑一个文件 仅导出 React 组件,快速刷新将仅更新该文件的代码,并重新渲染您的组件。 您可以编辑该文件中的任何内容,包括样式、渲染逻辑、事件处理程序或效果。”

  • “如果你 编辑包含非 React 组件导出的文件,快速刷新将重新运行该文件,以及导入它的其他文件。 因此,如果 Button.js 和 Modal.js 都导入 theme.js,则编辑 theme.js 将更新这两个组件。”

  • “最后, 如果您编辑由 React 树外部的文件导入的文件,快速刷新将回退到完全重新加载。 您可能有一个文件,它呈现 React 组件,但也导出由非 React 组件导入的值。 例如,也许您的组件还导出一个常量,并且非 React 实用程序文件导入它。 在这种情况下,请考虑将常量迁移到单独的文件并将其导入到两个文件中。 这将重新启用快速刷新功能。 其他案件通常可以用类似的方式解决。”

有时也可能不需要快速刷新。 为此,您可以设置 // @refresh reset 在您正在处理的文件中强制完全重新加载。


instrumentation.ts

如果你有一个名为 instrumention.ts 在根目录中(或在 src),它将运行一个名为的导出函数 register 当 NextJS 服务器最初启动时。

您可以使用它来设置/运行必须初始化才能运行应用程序的外部代码。

这是一个例子:

import { init } from 'package-init' export function register() { init() }


输入检查您的 next.config.js 文件

您的文件扩展名必须为 .js 为您 next.config.js 文件,因此您无法获得完整的 Typescript 类型检查。 但是,如果添加以下行,那么您的 IDE 可能会运行一些类型检查:

next.config.js

✅ copied

// @ts-check const nextConfig = { /* config options here */ } module.exports = nextConfig


中间件的 waitUntilNextFetchEvent (应用程序路由器)

如果您正在使用中间件并希望在后台运行一些代码,那么您可以使用 waitUntil。 这是一个例子:

middlware.ts

✅ copied

import { NextResponse } from 'next/server' import type { NextFetchEvent, NextRequest } from 'next/server' export function middleware(req: NextRequest, event: NextFetchEvent) { event.waitUntil( fetch('https://my-analytics-platform.com', { method: 'POST', body: JSON.stringify({ pathname: req.nextUrl.pathname }), }) ) return NextResponse.next() }


多行并引用 .env 文件中的其他变量

对于本地开发环境,可以快速轻松地使用您的 .env 设置一些变量。 但有时您会需要多行值。 这可以通过您的 .env 文件,像这样:

PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- ... Kh9NV... ... -----END DSA PRIVATE KEY-----" PRIVATE_KEY_2="-----BEGIN RSA PRIVATE KEY-----nKh9NV...n-----END DSA PRIVATE KEY-----n"

您还可以像这样引用其他变量:

TWITTER_USER=nextjs TWITTER_URL=https://twitter.com/$TWITTER_USER

在上面的示例中,TWITTER_URL 将设置为“https://twitter.com/nextjs”


航线段控制

如果您使用的是应用程序路由器,则从您的应用程序路由器中导出一些值 page.tsx, layout.tsx 或者 route.ts 可以配置该路由。

如果您使用过应用程序路由器,这可能是众所周知的,但我认为看到您可以配置的所有导出值很有趣。

export const dynamic = 'auto' export const dynamicParams = true export const revalidate = false export const fetchCache = 'auto' export const runtime = 'nodejs' export const preferredRegion = 'auto' export const maxDuration = 5 export default function MyComponent() {}

顺便说一句,这些值必须是静态的 – 它们是在运行时之前提取的。 所以, export const maxDuration = 5 会起作用,但是 export const maxDuration = 2.5 * 2 将不会。

对于每个配置的文档,我建议 读这个


您可能想要设置一些元数据,例如:

您可以轻松地在服务器组件上完成此操作 生成视口()

export function generateViewport({ params }) { return { themeColor: 'black', width: 'device-width', initialScale: 1, maximumScale: 1, colorScheme: 'dark', } /* name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> */ }


使用 isBot 检查请求是否来自已知的机器人,等等

如果你导入 userAgentnext/server,它可用于告诉您传入请求的用户代理。 您可以用它做一些有趣的事情:

  • isBot 返回的布尔属性将告诉您它是否是已知的爬虫/机器人
  • device 布尔值以了解用户代理正在运行的设备(例如移动电话、台式机)。 它包括以下属性 model (设备型号), type (控制台/手机/平板电脑/等),以及 vendor
middleware.ts

✅ copied

import { NextRequest, NextResponse, userAgent } from 'next/server' export function middleware(request: NextRequest) { const { isBot } = userAgent(request) if(isBot) { // do something for these bot users } return NextResponse.rewrite(request.nextUrl) }


next.config.js 阶段

在你的 next.config.js 文件中,您有时可能希望为不同的上下文返回不同的配置(构建/服务器/开发/测试

这显示了如何使用它:

next.config.js

✅ copied

const { PHASE_DEVELOPMENT_SERVER } = require('next/constants') module.exports = (phase, { defaultConfig }) => { /* possible values: PHASE_EXPORT = 'phase-export' PHASE_PRODUCTION_BUILD = 'phase-production-build' PHASE_PRODUCTION_SERVER = 'phase-production-server' PHASE_DEVELOPMENT_SERVER = 'phase-development-server' PHASE_TEST = 'phase-test' */ if (phase === PHASE_DEVELOPMENT_SERVER) { return { /* development only config options here */ } } return { /* config options for all phases except development here */ } }


构建时获取更多调试信息

如果你跑 next build --debug 从你的命令行它将在调试模式下构建。

你也可以运行 next build --profile 以配置文件模式运行。

next info 将打印出本地平台信息,例如操作系统和架构。


全局错误页面(应用程序路由器)

我们大多数人都知道 layout.ts, 而且当然 page.tsx。 还有一些其他特殊文件名,包括 error.tsglobal-error.ts

  • app/error.ts border 不会捕获根 app/layout.js 或 app/template.js 组件中抛出的错误。
  • 但你可以使用 app/global-error.ts 抓住这些。 它包装了整个应用程序。
  • 因为它包裹了整个应用程序 – 包括布局文件 – 你必须包括 global-error.ts

在这里阅读更多内容 (和 这里 用于页面路由器的错误处理)。


使用应用程序路由器时,您应该使用元数据 API 设置元标签或标题标签(不要只是将它们添加到 你自己)。

import { Metadata } from 'next' // either Static metadata export const metadata: Metadata = { title: '...', } // or Dynamic metadata export async function generateMetadata({ params }) { return { title: '...', } }

查看他们的文档

注意:页面路由器也存在类似的文件,该文件更常用 – pages/_error.js


组件有一些布尔选项

我们都知道 组件,导入自 'next/link'

这是您通常使用它的方式:

import Link from 'next/link' export default function YourPage() { return <Link href="/home"> Go home Link> }

以及 href 属性,还有一些布尔属性:

替换 NextJS Link 组件上的布尔选项

首先是 replace。 默认为 replace={false} 所以你只需要在将其设置为 true 时设置它。

当 true 时,next/link 将替换当前历史记录状态,而不是将新 URL 添加到浏览器历史记录中

import Link from 'next/link' export default function YourPageReplace() { return ( <Link href="/home" replace> Go home (replace) Link> ) }

你也可以这样做 useRouter(): router.replace('/home')

链接组件上的滚动选项

这是另一个默认为 false 的布尔值,因此只有在将其设置为 true 时才需要设置它。

您可能已经注意到,当您单击 NextJS 中的链接时,它会滚动到页面顶部(以使其看起来像典型的 当它从头开始加载整个页面时)。

有时您可能想阻止这种行为并保持在当前滚动位置。 您可以使用 scroll 布尔值支持 :

page-scroll.tsx

✅ copied

import Link from 'next/link' export default function Page() { return ( <Link href="/dashboard" scroll> Dashboard (scroll) Link> ) }

使用时也可以关闭自动滚动 useRouter()push() 功能: router.push('/dash', { scroll: false })

请注意,您仍然可以在 URL 中使用锚点,如下所示:

<Link href="/dashboard#profile">ProfileLink>

预取

当。。。的时候 prefetch 布尔值已打开(默认情况下),链接的 href URL 将在后台预取。

对于当前视口中的链接或在页面上滚动后可见的链接,将会发生此预取。

page-no-prefetch.tsx

✅ copied

import Link from 'next/link' export default function Page() { return ( <> <Link href="/page1" prefetch={false}> Dashboard (will not be prefetched) Link> <Link href="/page2"> Dashboard (default behaviour - with prefetch enabled) Link> > ) }

预取也可以通过以下方式完成 useRouter(), 例如 router.prefetch('/page2')


配置浏览器列表以针对旧版浏览器进行填充

NextJS 将为最新的浏览器自动填充(并在需要时自动添加 CSS 前缀) autoprefixer,但是您可以通过添加配置来专门覆盖它将填充的版本 package.json:

package.json

✅ copied

{ "browserslist": [ "chrome 64", "edge 79", "firefox 67", "opera 51", "safari 12" ] }


您不必运行标准的 Next 服务器

如果您对应用程序有复杂的要求并且想要使用 NextJS,您可能会从设置自定义 Next 服务器中受益。

例如,您可以有一个 Express 应用程序,它通过标准 NextJS 渲染逻辑运行某些路由,但通过其自己的逻辑运行其他路由。

这是一个例子

const { createServer } = require('http') const { parse } = require('url') const next = require('next') const dev = process.env.NODE_ENV !== 'production' const hostname = 'localhost' const port = 3000 // when using middleware `hostname` and `port` must be provided below const app = next({ dev, hostname, port }) const handle = app.getRequestHandler() app.prepare().then(() => { createServer(async (req, res) => { // Be sure to pass `true` as the second argument to `url.parse`. // This tells it to parse the query portion of the URL. const parsedUrl = parse(req.url, true) const { pathname, query } = parsedUrl if (pathname === '/a') { await app.render(req, res, '/a', query) } else { await handle(req, res, parsedUrl) } }) .listen(port, () => { console.log(`> Ready on http://${hostname}:${port}`) }) })

这方面没有大量的文档,但是 这个堆栈溢出 解释 getRequestHandler 函数。

有一些很好的例子来说明如何使用它 在 Github 上


全局错误页面(应用程序路由器)

我们大多数人都知道 layout.ts, 而且当然 page.tsx。 还有一些其他特殊文件名,包括 error.tsglobal-error.ts

  • app/error.ts border 不会捕获根 app/layout.js 或 app/template.js 组件中抛出的错误。
  • 但你可以使用 app/global-error.ts 抓住这些。 它包装了整个应用程序。
  • 因为它包裹了整个应用程序 – 包括布局文件 – 你必须包括 global-error.ts

在这里阅读更多内容 (和 这里 用于页面路由器的错误处理)。


使用应用程序路由器时,您应该使用元数据 API 设置元标签或标题标签(不要只是将它们添加到 你自己)。

import { Metadata } from 'next' // either Static metadata export const metadata: Metadata = { title: '...', } // or Dynamic metadata export async function generateMetadata({ params }) { return { title: '...', } }

查看他们的文档

注意:页面路由器也存在类似的文件,该文件更常用 – pages/_error.js


多区域(部署多个 Next.JS 应用程序)

如果您有一个大型应用程序(或现有应用程序),您可能希望将它们拆分为完全独立的应用程序并拥有自己的部署。

一个例子是 http://yoursite.com/blog 这是它自己的博客应用程序,然后该域上的其他所有内容都作为不同的应用程序。

您可以在您的 next.config.js 在您的主应用程序中,进行重写。 如果您有 2 个应用程序 homeblog, 和 home 作为主要应用程序:

next.config.js (in main app)

✅ copied

const { BLOG_URL } = process.env // e.g. http://localhost:9999/ module.exports = { async rewrites() { return [ { source: '/:path*', destination: `/:path*`, }, { source: '/blog', destination: `${BLOG_URL}/blog`, }, { source: '/blog/:path*', destination: `${BLOG_URL}/blog/:path*`, }, ] }, }

另一个的 next.config.js (blog)应用程序只需要设置一个 basePath: '/blog

然后,当您运行主应用程序(已重写)时,您将在 localhost:9999,但是博客应用程序上 localhost:9999。 当您访问时 http://localhost:3000/blog/hi 它将被代理来自 http://localhost:9999/hi

因此,访问您网站的人不会知道部署了两个单独的应用程序。 当您为站点提供服务时,这需要更多配置,但如果您使用 Vercel,则设置起来很容易。

查看 这里有一个演示应用程序


从 getServerSideProps() 返回 404 或重定向(页面路由器)

这不完全是隐藏功能或任何东西,但我看到一些应用程序在组件中显示 404(而不是在调用中) getServerSideProps())。 返回 404 状态很容易:

pages/somePage.tsx

✅ copied

export async function getServerSideProps(context) { const yourData = await getYourData() if (!yourData.isSuccess) { return { notFound: true } } // otherwise continue as normal, // send yourData as props to component: return { props: { yourData } } }

您还可以返回一些其他特殊属性,例如强制重定向:

pages/somePage.tsx (getServerSideProps)

✅ copied

// ... if(yourData.shouldRedirect) { return { redirect: { destination: '/', permanent: false, }, } } // ...

如果您正在使用 getStaticProps 然后你也可以以类似的方式强制 404 或重定向。 看看他们的 关于 getStaticProps 的文档在这里


Leave a Reply

Your email address will not be published. Required fields are marked *

近期新闻​

编辑精选​