support-prerendering
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSupport Prerendering
支持预渲染
How Prerendering Works
预渲染的工作原理
Prerendering is on by default for all interactive render modes. The server renders the component as static HTML and ships it to the browser immediately. Then the interactive runtime (Server/WebAssembly) loads and re-renders the component with full interactivity.
This means:
- runs twice — once during prerender (static), once when the interactive runtime attaches.
OnInitializedAsync - is NOT called during prerender — only after the interactive render.
OnAfterRenderAsync - Internal navigation between interactive pages (interactive routing) skips prerendering — prerendering only happens on full page loads.
所有交互式渲染模式默认启用预渲染。服务器将组件渲染为静态HTML并立即发送到浏览器。随后,交互式运行时(Server/WebAssembly)加载并重新渲染组件,使其具备完整交互性。
这意味着:
- 会运行两次——一次在预渲染(静态)阶段,一次在交互式运行时连接后。
OnInitializedAsync - 在预渲染阶段不会被调用——仅在交互式渲染后调用。
OnAfterRenderAsync - 交互式页面之间的内部导航(交互式路由)会跳过预渲染——预渲染仅在整页加载时发生。
Step 1 — Read the Project's AGENTS.md
步骤1 — 阅读项目的AGENTS.md
Check the project's for the Interactivity Mode and Interactivity Scope:
AGENTS.md| Mode | Prerendering applies? |
|---|---|
| None (Static SSR) | No — there's no interactive handoff |
| Server | Yes |
| WebAssembly | Yes |
| Auto | Yes |
If the mode is , this skill doesn't apply.
None查看项目的文件,了解交互模式和交互范围:
AGENTS.md| 模式 | 是否应用预渲染? |
|---|---|
| None(静态SSR) | 否——不存在交互式切换 |
| Server | 是 |
| WebAssembly | 是 |
| Auto | 是 |
如果模式为,则本技能不适用。
NonePersist State Across Prerender → Interactive
跨预渲染→交互式阶段持久化状态
The most common prerendering problem: data loaded in during prerender is thrown away and re-fetched when the interactive runtime attaches. This causes flicker and duplicate API/DB calls.
OnInitializedAsync最常见的预渲染问题:预渲染阶段在中加载的数据会被丢弃,在交互式运行时连接后会重新获取。这会导致页面闪烁和重复的API/数据库调用。
OnInitializedAsyncRecommended: [PersistentState]
attribute
[PersistentState]推荐方案:[PersistentState]
特性
[PersistentState]Annotate properties to automatically serialize during prerender and restore on interactive activation:
razor
@page "/forecasts"
@rendermode InteractiveServer
<h1>Weather</h1>
@if (Forecasts is null)
{
<p>Loading...</p>
}
else
{
@foreach (var f in Forecasts)
{
<p>@f.Date: @f.TemperatureC°C</p>
}
}
@code {
[PersistentState]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastsAsync();
}
}The pattern is critical — it means "only fetch if the property wasn't already restored from prerender state."
??=为属性添加注解,使其在预渲染期间自动序列化,并在交互式激活时恢复:
razor
@page "/forecasts"
@rendermode InteractiveServer
<h1>Weather</h1>
@if (Forecasts is null)
{
<p>Loading...</p>
}
else
{
@foreach (var f in Forecasts)
{
<p>@f.Date: @f.TemperatureC°C</p>
}
}
@code {
[PersistentState]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastsAsync();
}
}??=Multiple instances of the same component
同一组件的多个实例
When the same component type appears multiple times, use to disambiguate state:
@keyrazor
@foreach (var item in items)
{
<ItemCard @key="item.Id" />
}当同一组件类型多次出现时,使用区分状态:
@keyrazor
@foreach (var item in items)
{
<ItemCard @key="item.Id" />
}Advanced: PersistentComponentState
service
PersistentComponentState进阶方案:PersistentComponentState
服务
PersistentComponentStateFor complex scenarios (dynamic keys, custom serialization), use the imperative API:
csharp
@inject PersistentComponentState ApplicationState
@code {
private List<Order>? orders;
protected override async Task OnInitializedAsync()
{
ApplicationState.RegisterOnPersisting(PersistOrders);
if (!ApplicationState.TryTakeFromJson<List<Order>>("orders", out var restored))
{
orders = await OrderService.GetOrdersAsync();
}
else
{
orders = restored;
}
}
private Task PersistOrders()
{
ApplicationState.PersistAsJson("orders", orders);
return Task.CompletedTask;
}
}对于复杂场景(动态键、自定义序列化),使用命令式API:
csharp
@inject PersistentComponentState ApplicationState
@code {
private List<Order>? orders;
protected override async Task OnInitializedAsync()
{
ApplicationState.RegisterOnPersisting(PersistOrders);
if (!ApplicationState.TryTakeFromJson<List<Order>>("orders", out var restored))
{
orders = await OrderService.GetOrdersAsync();
}
else
{
orders = restored;
}
}
private Task PersistOrders()
{
ApplicationState.PersistAsJson("orders", orders);
return Task.CompletedTask;
}
}Disable Prerendering
禁用预渲染
Disable prerendering when a component depends on browser APIs immediately or when the prerender+interactive double render causes problems you can't solve with .
[PersistentState]当组件需要立即依赖浏览器API,或者预渲染+交互式双重渲染导致的问题无法通过解决时,可禁用预渲染。
[PersistentState]On a component definition
在组件定义上禁用
razor
@rendermode @(new InteractiveServerRenderMode(prerender: false))Replace with or as needed.
InteractiveServerRenderModeInteractiveWebAssemblyRenderModeInteractiveAutoRenderModerazor
@rendermode @(new InteractiveServerRenderMode(prerender: false))根据需要将替换为或。
InteractiveServerRenderModeInteractiveWebAssemblyRenderModeInteractiveAutoRenderModeOn a component instance
在组件实例上禁用
razor
<MyChart @rendermode="new InteractiveServerRenderMode(prerender: false)" />razor
<MyChart @rendermode="new InteractiveServerRenderMode(prerender: false)" />On the entire app
在整个应用中禁用
In :
App.razorrazor
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />Note: A parent's prerendering setting overrides children. If disables prerendering, individual pages cannot re-enable it.
<Routes>在中:
App.razorrazor
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />注意:父组件的预渲染设置会覆盖子组件。如果禁用了预渲染,单个页面无法重新启用它。
<Routes>Exclude Pages from Interactive Routing
将页面排除在交互式路由之外
In a globally interactive app, some pages may need (cookies, request headers, response status codes). These pages must render via static SSR, not inside the interactive runtime.
HttpContextUse :
[ExcludeFromInteractiveRouting]razor
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]
<h1>Privacy Policy</h1>This forces a full page reload when navigating to this page, exiting interactive routing. The page renders as static SSR with full access.
HttpContextIn , conditionally apply the render mode:
App.razorrazor
<!DOCTYPE html>
<html>
<head>
<HeadOutlet @rendermode="RenderModeForPage" />
</head>
<body>
<Routes @rendermode="RenderModeForPage" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
@code {
[CascadingParameter]
public HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? RenderModeForPage =>
HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}Replace with the app's configured render mode.
InteractiveServer在全局交互式应用中,某些页面可能需要(Cookie、请求头、响应状态码)。这些页面必须通过静态SSR渲染,而不是在交互式运行时内部渲染。
HttpContext使用特性:
[ExcludeFromInteractiveRouting]razor
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]
<h1>Privacy Policy</h1>这会强制导航到该页面时进行整页重载,退出交互式路由。页面将以静态SSR方式渲染,并可完全访问。
HttpContext在中,有条件地应用渲染模式:
App.razorrazor
<!DOCTYPE html>
<html>
<head>
<HeadOutlet @rendermode="RenderModeForPage" />
</head>
<body>
<Routes @rendermode="RenderModeForPage" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
@code {
[CascadingParameter]
public HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? RenderModeForPage =>
HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}将替换为应用配置的渲染模式。
InteractiveServerDetect Prerender vs Interactive at Runtime
在运行时检测预渲染与交互式状态
Use to guard code that should only run interactively:
RendererInfocsharp
protected override async Task OnInitializedAsync()
{
if (RendererInfo.IsInteractive)
{
// Only runs during the interactive render, not during prerender
await StartSignalRConnection();
}
}RendererInfo- —
IsInteractiveduring prerender,falseafter interactive runtime attachestrue - —
Nameduring prerender,"Static"or"Server"when interactive"WebAssembly"
使用保护仅应在交互式阶段运行的代码:
RendererInfocsharp
protected override async Task OnInitializedAsync()
{
if (RendererInfo.IsInteractive)
{
// 仅在交互式渲染阶段运行,预渲染阶段不执行
await StartSignalRConnection();
}
}RendererInfo- — 预渲染阶段为
IsInteractive,交互式运行时连接后为falsetrue - — 预渲染阶段为
Name,交互式阶段为"Static"或"Server""WebAssembly"
Client Services Fail During Prerender
客户端服务在预渲染期间失败
Components in the project prerender on the server. Services registered only in the client (e.g., ) won't be available during prerender.
.ClientProgram.csIWebAssemblyHostEnvironmentFix by one of:
- Register a matching service on the server — both files provide the service
Program.cs - Make the service optional — use constructor injection with a nullable default:
public MyComponent(IMyService? svc = null) - Create a service abstraction — interface in , implementations in both projects
.Client - Disable prerendering for that component
.ClientProgram.csIWebAssemblyHostEnvironment可通过以下方式修复:
- 在服务器上注册匹配的服务 — 两个文件都提供该服务
Program.cs - 将服务设为可选 — 使用构造函数注入并设置可空默认值:
public MyComponent(IMyService? svc = null) - 创建服务抽象 — 在中定义接口,在两个项目中实现
.Client - 禁用该组件的预渲染
Don'ts
注意事项
- Don't call JS interop in — JS isn't available during prerender. Use
OnInitializedAsync.OnAfterRenderAsync(firstRender) - Don't assume runs once — it runs twice with prerendering. Always use
OnInitializedAsyncor[PersistentState]guards.??= - Don't use in interactive components — it's only available during the static prerender, not during the interactive lifetime. Use
HttpContextfor pages that need it.[ExcludeFromInteractiveRouting] - Don't disable prerendering as a first resort — it hurts perceived load time and SEO. Use to preserve state instead.
[PersistentState]
- 不要在中调用JS interop——预渲染期间JS不可用。请使用
OnInitializedAsync。OnAfterRenderAsync(firstRender) - 不要假设仅运行一次——在预渲染模式下它会运行两次。请始终使用
OnInitializedAsync或[PersistentState]进行保护。??= - 不要在交互式组件中使用——它仅在静态预渲染阶段可用,在交互式生命周期中不可用。对需要
HttpContext的页面使用HttpContext。[ExcludeFromInteractiveRouting] - 不要将禁用预渲染作为首选方案——这会损害感知加载时间和SEO。请改用来保留状态。
[PersistentState]