nextjs-rendering

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Next.js Rendering Expert Guidance

Next.js渲染专家指导

Core Concepts

核心概念

Static vs Dynamic Rendering

静态与动态渲染

Understand the fundamental distinction between static and dynamic rendering:
  • Static pages do not involve compute. They are pre-built, uploaded to a CDN, and replicated to edge locations globally. This means static content remains available even if a region goes down and serves faster because it's cached near the user with no compute overhead.
  • Dynamic pages execute in a specific region where the serverless function is configured to run. They require compute on every request, making them slower and dependent on regional availability.
  • SSG and ISR are the same rendering mode. The only difference is whether you generate all paths upfront (SSG) or incrementally on-demand (ISR). Both produce static output.
  • Cache components can combine static and dynamic behavior, but the page itself is still fundamentally either static or dynamic. Even with cache components, the underlying render mode matters.
了解静态渲染与动态渲染的根本区别:
  • 静态页面不涉及计算。它们是预构建的,会被上传到CDN并复制到全球边缘节点。这意味着即使某个区域出现故障,静态内容依然可用,并且由于内容缓存在用户附近,无需计算开销,加载速度更快。
  • 动态页面在特定区域执行,该区域是无服务器函数配置的运行区域。它们在每次请求时都需要进行计算,因此速度更慢,并且依赖于该区域的可用性。
  • SSG和ISR属于同一种渲染模式。唯一的区别在于是否预先生成所有路径(SSG),还是按需增量生成(ISR)。两者都会生成静态输出。
  • 缓存组件可以结合静态与动态行为,但页面本身从根本上讲仍然是静态或动态的。即使使用缓存组件,底层的渲染模式也至关重要。

The Prospective Render System

预期渲染系统

Next.js uses a two-phase render to determine what is static vs dynamic:
  1. Warmup render (prospective render): Next.js searches for cached instances and fills those caches. This phase identifies what can be resolved synchronously.
  2. Microtask check: Next.js runs a second render that executes for just one tick, then aborts. Everything that resolves within that single microtask is considered "instant" and therefore static.
This two-render mechanism is how Next.js determines whether components are async or not without explicit developer annotation.
Next.js使用两阶段渲染来区分静态与动态内容:
  1. 预热渲染(预期渲染):Next.js会查找缓存实例并填充这些缓存。此阶段会识别哪些内容可以同步解析。
  2. 微任务检查:Next.js会运行第二次渲染,仅执行一个任务周期后就中止。在单个微任务内解析的所有内容都被视为“即时”内容,因此属于静态内容。
这种双渲染机制让Next.js无需开发者显式标注,就能判断组件是否为异步组件。

Streaming and Suspense

流式处理与Suspense

Streaming Behavior in Static Rendering

静态渲染中的流式处理行为

Static content does not support streaming because no compute is involved at request time. The entire page is pre-generated and served as a complete asset.
  • Suspense boundary fallbacks will not show when generating ISR paths. The page is built fully before being cached, so users never see intermediate loading states.
  • Streaming only works with dynamic rendering where the server can flush partial content while waiting for async operations.
静态内容不支持流式处理,因为请求时不涉及计算。整个页面是预先生成的,作为完整资源提供给用户。
  • 生成ISR路径时,Suspense边界的回退内容不会显示。页面会在缓存前完全构建完成,因此用户永远不会看到中间加载状态。
  • 流式处理仅适用于动态渲染,此时服务器可以在等待异步操作完成时刷新部分内容。

Enabling Streaming for Dynamic Pages

为动态页面启用流式处理

To allow the server to stream a page without requiring a complete skeleton upfront:
jsx
<Suspense fallback={null}>
  {/* Wrap your entire HTML document */}
  <html>
    <body>{children}</body>
  </html>
</Suspense>
Wrap the HTML document with an empty Suspense boundary. Without a document element outside the boundary, there's nothing to stream—the server needs an outer shell to flush to the client.
要允许服务器在不需要预先生成完整页面骨架的情况下流式传输页面,请使用以下方式:
jsx
<Suspense fallback={null}>
  {/* 包裹整个HTML文档 */}
  <html>
    <body>{children}</body>
  </html>
</Suspense>
用空的Suspense边界包裹HTML文档。如果边界外没有文档元素,就没有可流式传输的内容——服务器需要一个外部外壳来向客户端刷新内容。

Determining Static vs Dynamic

区分静态与动态

Async Dynamic APIs

异步动态API

Next.js uses async dynamic APIs as signals to determine when something must be dynamic:
  • Anything that cannot run in a single tick becomes dynamic. If an operation requires awaiting beyond the initial microtask, Next.js marks that render as dynamic.
  • Exception:
    params
    can still create static routes
    because
    params
    is called once per item returned by
    generateStaticParams
    . Since each invocation is synchronous within that static generation context, it's considered "instant."
tsx
// This can still be static if generateStaticParams provides the params
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }];
}

export default async function Page({ params }: { params: { id: string } }) {
  // params is "instant" here because it's called once per static param
  const { id } = params;
  // ...
}
Next.js将异步动态API作为信号,来判断内容是否必须为动态:
  • 任何无法在单个任务周期内完成的操作都会被标记为动态。如果某个操作需要等待超出初始微任务的时间,Next.js会将该渲染标记为动态。
  • 例外情况:
    params
    仍可创建静态路由
    ,因为
    params
    会针对
    generateStaticParams
    返回的每个条目调用一次。由于每次调用都在静态生成上下文中同步执行,因此被视为“即时”操作。
tsx
// 如果generateStaticParams提供了params,这仍然可以是静态路由
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }];
}

export default async function Page({ params }: { params: { id: string } }) {
  // 这里的params是“即时”的,因为它针对每个静态参数调用一次
  const { id } = params;
  // ...
}

Using Cache Directives

使用缓存指令

"use cache" Behavior

"use cache"行为

Adding
"use cache"
to a page or its main component fundamentally changes its output:
tsx
'use cache';

export default function Page() {
  // This produces static output
}
When you add "use cache" to a page component, you are producing static output that will be:
  • Cached and reused
  • Not streamed (served as complete HTML)
  • Not have holes or Suspense boundaries that resolve at request time
Even in a dynamic app, cache directives convert specific subtrees to static behavior with all the implications of static rendering (no streaming, no runtime Suspense resolution).
在页面或其主组件中添加
"use cache"
会从根本上改变其输出:
tsx
'use cache';

export default function Page() {
  // 这会生成静态输出
}
当你在页面组件中添加"use cache"时,会生成静态输出,该输出将:
  • 被缓存并重复使用
  • 不支持流式处理(作为完整HTML提供)
  • 不会存在在请求时解析的占位内容或Suspense边界
即使在动态应用中,缓存指令也会将特定子树转换为静态行为,并伴随静态渲染的所有特性(无流式处理、无运行时Suspense解析)。

Common Pitfalls

常见误区

Expecting Streaming in Static Contexts

期望在静态上下文中使用流式处理

Do not expect Suspense fallbacks or streaming to work when:
  • Generating ISR paths
  • Using
    "use cache"
    on page components
  • Building SSG routes
In all these cases, the page is fully rendered during build/revalidation, not at request time.
在以下场景中,不要期望Suspense回退内容或流式处理能够生效:
  • 生成ISR路径时
  • 在页面组件上使用
    "use cache"
  • 构建SSG路由时
在所有这些场景中,页面会在构建/重新验证阶段完全渲染完成,而不是在请求时。

Misunderstanding Regional Failures

误解区域故障的影响

Remember that dynamic content fails if its configured region goes down, while static content remains available globally. Design critical paths as static when high availability is required.
请记住,如果动态内容的配置区域出现故障,动态内容将无法访问,而静态内容在全球范围内依然可用。当需要高可用性时,请将关键路径设计为静态内容。

Forgetting the Prospective Render

忽略预期渲染机制

When debugging why something is marked as dynamic, remember that Next.js aborts a render after one tick to detect async work. If your code performs any async operation that doesn't resolve in a microtask, it becomes dynamic, even if it seems "fast."
当调试某内容为何被标记为动态时,请记住Next.js会在一个任务周期后中止渲染,以检测异步工作。如果你的代码执行了任何无法在微任务中解析的异步操作,即使看起来“很快”,该内容也会被标记为动态。