这是用户在 2024-5-7 14:30 为 https://dev.to/perssondennis/answers-to-common-nextjs-questions-1oki 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

 DEV 社区

Cover image for Answers to Common Next.js Questions
Dennis Persson
Dennis Persson


发布于 5 月 5 日 • 更新于 5 月 7 日 • 最初发表于 perssondennis.com

12 4 4 3 6


关于常见的 Next.js 问题的答案


Web 开发从服务器渲染的应用程序转变为完全在客户端渲染的单页面应用程序,然后一切变得复杂起来。Next.js 现在已经清除了关于客户端和服务器的旧视角,为我们提供了一个高性能框架,这是一个包含客户端和服务器的大黑匣子。但不用担心,我会在这里提供关于 Next.js 及其服务器组件的常见问题的 FAQ。

Frontend devs got tricked

至少我们可以用 JavaScript 编写服务器代码


本文目录


  1. 如何在客户端组件中读取 URL 参数?

  2. 如何在页面文件中读取 URL 参数?

  3. 如何在 Next.js 布局文件中读取 URL 参数?

  4. 如何在服务器组件中读取 URL 参数?

  5. 为什么在 Next.js 布局文件中不存在 searchParams?

  6. 如何在 Next.js 中跳过某些页面的根布局?

  7. 如何在应用路由器中设置元数据标签?

  8. 如何为应用程序路由器页面动态设置元数据?

  9. 我可以在布局文件中设置元数据吗?

  10. 我可以在应用路由器中使用上下文吗?

  11. 如何在服务器组件中使用 React 上下文?

  12. 是否可能在服务器组件中呈现客户端组件?

  13. 我可以从服务器组件传递属性到客户端组件吗?

  14. 我可以在客户端组件中使用服务器组件吗?

  15. 我可以从客户端组件传递属性到服务器组件吗?

  16. 我可以在服务器组件中使用服务器操作吗?

  17. 我可以在客户端组件中使用服务器操作吗?

  18. Next.js 中的服务器操作稳定吗?


1. 如何在客户端组件中读取 URL 参数?


如果您需要在客户端组件中读取路径名、查询参数或从 URL 获取语言环境,则可以使用 useRouter 函数来实现。

'use client'

import { useRouter } from 'next/navigation'

const ClientComponent({ children, href }) {
  const router = useRouter()

  return (
    <div>Next.js useRouter example</div>
    <div>{router.pathname}</div>
    <div>{JSON.stringify(router.query)}</div>
    <div>{router.locale}</div>
  )
}

export default ClientComponent
Enter fullscreen mode Exit fullscreen mode


从 useRouter 返回的路由器对象也可以用于以编程方式进行导航,以防需要导航。对于普通导航,请使用 Next.js 内置的链接组件。

'use client'

import { useRouter } from 'next/navigation'

const ClientComponent({ children }) {
  const router = useRouter()

  const handleClick = () => {
    router.push('someUrl')
  }

  return (
    <div onClick={handleClick}>Click me</div>
  )
}

export default ClientComponent
Enter fullscreen mode Exit fullscreen mode


2. 如何在页面文件中读取 URL 参数?


参数会自动作为 props 传递给页面组件。您可以简单地使用提供的 prop。

const PageComponent({ params, searchParams }) {
  const { slug } = params

  return <div>Next.js URL params in page.tsx file</div>
}

export default PageComponent
Enter fullscreen mode Exit fullscreen mode


3. 如何在 Next.js 布局文件中读取 URL 参数?


如果您需要在布局组件文件中读取 URL 参数,则有两个选项。第一个选项是在布局中创建一个客户端组件,并在客户端组件中读取参数。


如果您使用 Next.js 并希望主要使用服务器组件,那么这可能听起来像是一个不好的选择,但实际上这是完全自然的。您的布局文件的其余部分仍然可以作为服务器组件呈现,只是客户端组件本身需要在客户端上呈现。


例如,如果您想要使用 URL 参数来使用芯片组件过滤博客上的文章,那么只有包含芯片的组件需要是客户端组件,标题的其余部分可以保持为服务器组件。

Client component in server component header

在上述网站中,红色矩形可以是服务器组件,而依赖于 URL 参数的蓝色矩形可以是客户端组件。


另一种选择可能是较差的选择。这是将中间件添加到您的应用程序中,该中间件读取 URL 参数并将其传递给布局组件。这将使您可以灵活访问 URL 参数,但会使服务器组件失去静态渲染和客户端缓存的能力。


4. 如何在服务器组件中读取 URL 参数?


在任意服务器组件中,既不是布局组件也不是页面组件,您可以通过从其他组件传递它们作为属性来获取 URL 参数。


需要记住的重要一点是,并非页面文件组件的服务器组件在导航期间通常不会刷新,整个组件会被序列化并发送到客户端,仅在极少数情况下才会更新。因此,在服务器组件中使用来自 URL 的信息时必须小心。在导航过程中,URL 参数将过时。


5. 为什么 Next.js 布局文件中没有 searchParams 存在?


由于某种原因,searchParams 仅在页面文件中可用。页面文件在两个页面之间导航时将始终重新呈现。由于组件在 URL 更改时重新呈现,我们可以确定传递给组件的 URL 参数是最新的。


布局组件的目的是避免不必要的重新渲染,并包含用户在页面之间导航时不会更新的 UI 的静态部分。因此,布局组件仅在用户导航到屏幕上不可见的组件的另一部分时才重新呈现。在代码中,这意味着在文件树中没有共享公共布局文件作为祖先的两个页面之间导航。


实际上,这意味着 URL 的更改频率要比布局文件的渲染频率更高,如果我们要在布局组件中读取 URL 参数,则该组件将无法得知何时更新 URL 参数。请记住,布局文件是一个服务器组件,当首次获取时会呈现为 HTML,然后在客户端缓存,因此不必重新呈现。


要了解更多信息,请阅读上面关于如何在 Next.js 布局文件中读取 URL 参数的部分。


如何在 Next.js 中跳过某些页面的根布局?


一个 Next.js 项目可以有许多布局文件,技术上每个页面都可以有自己的布局文件。包裹整个应用程序的顶层布局文件称为根布局。


布局文件允许您将一个组件重复使用为多个页面的包装组件。通常,每个 Next.js 应用程序都有一个根布局文件,其中包括应用程序的主菜单,例如标题菜单或英雄图像。


在这样做的过程中,您很快就会遇到一个问题;您是否可以跳过特定页面的根布局?答案是可以的。Next.js 有一个称为 Route Groups 的功能,允许您拥有多个根布局。这意味着应用程序的不同部分可以具有不同的布局,这也使得在某些页面的布局中完全不需要任何组件成为可能。


这里需要注意的一点是在路由组之间导航将触发完整的页面加载。这是替换应用程序中顶层服务器组件的自然结果。基本上意味着,您呈现应用程序的一个完全新的部分,这部分与所有其他页面都是独立的。


7. 如何在应用程序路由器中设置元数据标签?


Next.js 使您可以轻松为应用程序和页面设置元数据。您唯一需要做的就是从应用程序中的任何布局或页面文件中导出一个元数据对象,以设置该布局/页面及其以下的元数据。

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js Metadata Example',
  description: 'Example of metadata in Next.js',
}

export default function MyPage() { ... }
Enter fullscreen mode Exit fullscreen mode


如果您需要动态数据,请参阅下面的问题。


8. 如何为应用路由器页面动态设置元数据?


对于大多数经过 SEO 优化的应用程序来说,设置静态元数据是不够的,因为数据几乎每个页面都是唯一的,甚至在运行时从服务器加载。要在 Next.js 页面中动态设置元数据,请使用提供的 generateMetadata 函数。

import type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  const data = await fetch('someUrl').then((res) => res.json())

  return {
    title: 'Next.js Dynamic Metadata Example',
    description: data.pageDescription,
  }
}

export default function MyPage() { ... }
Enter fullscreen mode Exit fullscreen mode


需要了解的是,Next.js 中的 fetch 函数默认会缓存重复请求。这是一种常见行为,与元数据无关。即使在使用 fetch 时,服务器组件中的请求也会被缓存。尽管如此,当使用某些 Next.js 功能时,可以选择退出并自动禁用。


9. 我可以在布局文件中设置元数据吗?


是的,您可以在布局文件中设置元数据,无论是静态的还是动态的。请参阅上面关于设置元数据的问题。


10. 我可以在应用路由器中使用上下文吗?


在 Next.js 中可以使用 React 上下文与应用路由器一起使用。最好将上下文放置在根布局中,以使其对所有客户端组件可用。上下文将无法在服务器组件中使用,并且不会破坏服务器组件的渲染行为。


您可以在本文中了解其工作原理。


11. 如何在服务器组件中使用 React 上下文?


在 Next.js 中,您只能在客户端组件中使用 React 上下文。服务器组件不会受到客户端组件中上下文的影响,也无法读取上下文。有关更多信息,请参见“在 Next.js 13 中上下文是否使整个应用程序在客户端上呈现?”。


但是,根据您的需求,您可能不需要在服务器组件中使用上下文。默认情况下,Next.js 会缓存使用 fetch 函数发送的请求中检索到的数据,这意味着您不必担心重复的请求。如果多个服务器组件需要获取相同的数据,则可以依靠缓存作为通过上下文共享数据的替代方法。


在服务器组件中渲染客户端组件是可能的吗?


在服务器组件中渲染客户端组件是完全正常的。传递给客户端组件的所有 props 必须是可序列化的。无法序列化的数据必须在客户端组件内处理。


我可以将 props 从服务器组件传递给客户端组件吗?


是的,可以将 props 从服务器组件传递给客户端组件。如前一个问题所述,数据必须是可序列化的。


我可以在客户端组件中使用服务器组件吗?


在客户端组件中可以使用服务器组件,但有限制。您不能像通常那样在客户端组件中导入服务器组件,这样做时它们不会成为服务器组件。您需要做的是将服务器组件作为属性或子项传递给客户端组件。


特别注意,这个技巧让很多开发者困惑。Next.js 声称组件默认是服务器组件,而您必须使用"use client"指令将其转换为客户端组件。这并不是全部事实,因为当您在客户端组件中导入任何组件时,导入的组件将成为客户端组件,而不是服务器组件。


如上所述,如果应该将服务器组件视为服务器组件,则必须将其作为属性传递。Next.js 不会抱怨如果您将您认为是服务器组件导入到客户端组件中,它们只是简单地被视为客户端组件,而没有任何警告。因此,如果您认为自己使用了大量服务器组件,实际上您可能根本就没有那么多。


15. 我可以从客户端组件传递 props 到服务器端组件吗?


不,不可能从客户端组件传递 props 到服务器端组件。在客户端组件渲染之前,服务器端组件已经被渲染成 HTML。


16. 我可以在服务器组件中使用服务器操作吗?


当然可以,它们就是为此而设计的。请参阅文档中的示例。


客户端组件中可以使用服务器操作吗?


是的,但有一个注意事项。您不能直接在客户端组件中使用服务器操作。您可以将服务器操作写在单独的文件中,并在客户端组件中使用,就像文档中所示。


但是,如果您尝试直接在组件中使用服务器操作,就像在服务器组件中所做的那样,那么您将收到此错误。


不允许在客户端组件中定义内联的“使用服务器”注释的服务器操作。│要在客户端组件中使用服务器操作,您可以将它们从顶部带有“use server”注释的单独文件中导出,或者通过从服务器组件中通过 props 传递它们。


正如您在那里所读到的,您还有一个选择。如果您希望,您可以在服务器组件中定义服务器操作,并将其作为 prop 传递给客户端组件。


Next.js 中的服务器操作稳定吗?


在 Next.js 14 中,服务器操作被视为稳定的。但需要注意的是,Next.js 是建立在仍被标记为实验性的 React 特性之上的。


在 Next.js 13 中,服务器操作是实验性的,所以如果在版本 13 中,你将被要求将服务器操作添加为 next.config.js 中的实验性功能。


要使用服务器操作,请在你的 next.js 配置中启用该功能标志。


这是你需要添加到 next.config.js 中的内容。虽然我更推荐升级到 Next.js 14。

experimental: {
  serverActions: true,
},
Enter fullscreen mode Exit fullscreen mode


热门评论(9)

Collapse
 
pavelee profile image
Paweł Ciosek

 很棒的帖子!👏


在“我是否可以将 Props 从客户端组件传递到服务器端组件?”一节中,我们是否可以通过 URL 传递呢?


我的意思是,我们可以像这样做

// update your URL on client side
router.reload(); // refresh RSC components with new URL state
Enter fullscreen mode Exit fullscreen mode
Collapse
 
perssondennis profile image
Dennis Persson

 谢谢 :)


我想 router.refresh() 应该能够工作,因为它确实会刷新服务器组件。


尽管如此,我不建议这样做。我可能更愿意将服务器组件转换为客户端组件,并使用 useRouter 读取 URL(如果不需要将其持久保存在 URL 中,则将其作为 prop 传递)。


原因呢?因为这样,所有事情都会被自动处理。手动刷新页面当 URL 发生变化时是容易出错的,有一天这可能会变成一个 bug。


而且,如果您需要完全刷新您的服务器组件以将 URL 参数传递给它,从性能上讲,您可能更适合使用客户端组件。

Collapse
 
perssondennis profile image
Dennis Persson


感谢阅读!如果您想要我发布更多类似的文章,请通过撰写评论或使用一些反应来告诉我 ❤️🦄 🔥

Collapse
 
anmolbaranwal profile image
Anmol Baranwal


哇,喜欢这篇帖子。太多的 Next.js,哈哈!


你也可以查看这个,我不确定它是否有帮助,但它与 Nextjs 有关。


以后会仔细阅读!

Collapse
 
leandro_nnz profile image
Leandro Nuñez


很棒的文章。如果允许的话,我愿意添加一点修正:客户端组件也在服务器上渲染,但在客户端上被激活。这是一篇很棒的文章,可以理解:文章

Collapse
 
perssondennis profile image
Dennis Persson


是的,这是一个很好的补充,用于完整页面重新加载。不确定我是否写了一些暗示相反的东西,但无论如何,这是一个很好的观点,应该在这篇文章中提出问题。

Collapse
 
ricardogesteves profile image
Ricardo Esteves


useRouter 应该从 next/navigation 导入。

Collapse
 
perssondennis profile image
Dennis Persson


谢谢指出。我已更新。我的导入来自旧页面组件 :)

Collapse
 
monfernape profile image
Usman Khalil


是的,这清楚了很多事情🎯。 初始情况下很难理解 RSC,因为它们是一种不同的心理模型。