服务器组件渲染策略

🌐

本帖由 DeepL 翻译。如有任何翻译错误,请告知我们!

渲染服务器组件

客户端组件是 React 的传统组件,当其状态或其父组件的状态发生变化时就会渲染。但是,您不能在服务器组件上使用钩子,这意味着服务器组件没有状态。如下图所示,当父组件的状态发生变化时,服务器组件不会重新呈现。那么,服务器组件何时重新渲染?

AnimatedImage.gif

服务器组件渲染策略

根据 Next.js 官方文档,有三种渲染策略:静态(Static)、动态(Dynamic)和流(Streaming)。

静态渲染

静态渲染是 Next.js 的默认渲染策略。该策略的主要特点是在构建时对组件进行一次渲染,并重复使用渲染结果。

  • 构建时渲染

    • 服务器组件在应用程序构建时渲染。
    • 渲染结果会缓存在 CDN 上,并提供给所有用户。
  • 数据不变性

    • 一旦呈现,组件中的数据基本上是不可变的。
    • 页面刷新时显示的是相同的数据
    • 客户端状态变化不会影响服务器组件中的数据
  • 重新验证机制

    • 可通过revalidateTag``()revalidatePath()手动重新渲染
  1. 刷新屏幕时

    AnimatedImage.gif

    状态和重新加载屏幕不会更改数据。

  2. 何时重新验证

    AnimatedImage.gif

    运行 Next.js 中的revalidateTag时,我们可以看到它会使服务器组件的缓存失效,重新获取数据并重新渲染。

动态渲染

动态渲染是 Next.js 中的一种渲染策略,每次用户请求都会在服务器上渲染一个新的组件。

当满足以下两个条件之一时,服务器组件会自动切换到动态渲染

  1. 使用动态 API
  2. 在获取选项中找到{ cache: 'no-store' }

渲染策略决策表

| 使用动态 API|数据缓存|渲染结果 | -------------------- | --------------- | --------------- | --------------- | | ❌ 否 | ✅ 缓存 | 静态 | ✅ 是 | ✅ 缓存 | 静态 | ✅ 是 | ✅ 缓存 | 动态 | ✅ 是 | ❌ 否 ❌ 未缓存 | 动态 | ❌ 是 | ✅ 是 | ❌ 未缓存 | 动态 | ❌

这意味着您必须在不使用动态 API 的情况下对获取应用缓存,以实现静态渲染。

动态 API

动态 API 是依赖于请求时上下文的 API。让我们来看看主要的动态 API

1. cookie()

import { cookies } from 'next/headers'async 函数 Component() {
  const cookieStore = cookies();
  const theme = cookieStore.get('theme');
  返回 <div>当前主题:{theme?.value}</div>;
}

2. headers()

'next/headers'导入 { headers };

async 函数 Component() {
  const headersList = headers();
  const userAgent = headersList.get('user-agent');
  返回 <div>Accessing from: {userAgent}</div>;
}

搜索参数

// app/page.tsx
export default function Page({
  searchParams、
}:{
  searchParams: { query: string };
}){
  返回 <div>搜索查询:{searchParams.query}</div>;
}

与并行路由一起使用时的注意事项

使用 searchParams 控制模式窗时(如下面的示例),可能会遇到动态渲染问题。

  • 更改 searchParams 时服务器组件会重新渲染
  • 由于获取数据而导致模态延迟
  • 模式窗体打开的同时数据发生变化
// app/@modal/default.tsx
使用客户端

从 "next/navigation "导入 {useSearchParams};
import {Modal} from "@/app/@modal/(.)post/[id]/modal";

函数 Default() {
  const searchParam = useSearchParams();
  const isModalOpen = searchParam.get('modal') === 'true'if (!isModalOpen) {
    返回 null;
  }

  return (
    <模态>模态!!</模态
  );
}

} export default Default

AnimatedImage.gif

解决方法

最简单有效的方法是绕过 Next.js 的路由系统,直接使用浏览器内置的历史 API。

使用客户端

function ProductList() {
  const openModal = () => {
    // 绕过 Next.js 路由,直接更改 URL
    history.pushState(null, '', `?modal=true`);
  };

  返回 (
    <div> <button onClick={open
      <button onClick={openModal}>打开模态</button
    </div> </div>
  );
}

使用Link 或 useRouter会导致导航到已更改的 URL,从而运行新的呈现逻辑。但是,使用 History API 不会触发任何呈现逻辑,因为它只会更改 URL,而不会通过 Next 的路由系统,但它确实允许 Next 与useSearchParamsusePathname同步,这样它就能检测到 URL 的更改并显示模态。

结束语

服务器组件是 Next.js 中一种重要的呈现优化策略,但如果使用不当,可能会导致意外的重新呈现或性能下降。了解这些呈现策略并合理使用它们对于有效使用服务器组件非常重要。