关于 support-prerendering
面向交互式 Blazor 应用开发者的预渲染技能,涵盖状态保留、按需关闭预渲染,以及处理依赖交互运行时的代码。
主要功能
- 01预渲染生命周期说明
- 02跨渲染状态持久化
- 03按需控制预渲染
- 04为 HttpContext 使用静态路由
- 05运行时交互状态检测
- 06预渲染时配置客户端服务
适用场景
- 01避免重复获取数据
- 02在渲染后使用浏览器 API
- 03提供需要 HttpContext 的页面
- 04限制仅交互阶段运行的初始化代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill support-prerendering安装后在 Claude Code 中输入/support-prerendering确认已加载。
详细介绍
支持预渲染
预渲染的工作方式
所有交互式渲染模式默认启用预渲染。服务器会将组件渲染为静态 HTML,并立即发送到浏览器。随后,交互式运行时(Server/WebAssembly)会加载并重新渲染组件,使其具备完整的交互性。
这意味着:
OnInitializedAsync会运行两次——预渲染(静态)期间一次,交互式运行时接入时一次。- 预渲染期间不会调用
OnAfterRenderAsync——只会在交互式渲染后调用。 - 交互式页面之间的内部导航(交互式路由)会跳过预渲染——只有完整加载页面时才会进行预渲染。
步骤 1 — 阅读项目的 AGENTS.md
查看项目的 AGENTS.md,了解交互模式和交互范围:
| 模式 | 是否适用预渲染? |
|---|---|
| None(静态 SSR) | 否——没有交互式切换 |
| Server | 是 |
| WebAssembly | 是 |
| Auto | 是 |
如果模式为 None,则此技能不适用。
在预渲染 → 交互式运行期间保留状态
最常见的预渲染问题:在预渲染期间通过 OnInitializedAsync 加载的数据会被丢弃,并在交互式运行时接入时重新获取。这会导致闪烁,并产生重复的 API/数据库调用。
推荐:[PersistentState] 特性
为属性添加注解,以便在预渲染期间自动序列化,并在交互式激活时恢复:
@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();
}
}
??= 模式至关重要——它表示“仅当属性尚未从预渲染状态恢复时才获取数据”。
同一组件的多个实例
同一组件类型出现多次时,使用 @key 区分状态:
@foreach (var item in items)
{
<ItemCard @key="item.Id" />
}
高级用法:PersistentComponentState 服务
对于复杂场景(动态键、自定义序列化),请使用命令式 API:
@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,或者预渲染加交互式的双重渲染造成无法通过 [PersistentState] 解决的问题时,请禁用预渲染。
在组件定义上
@rendermode @(new InteractiveServerRenderMode(prerender: false))
根据需要,将 InteractiveServerRenderMode 替换为 InteractiveWebAssemblyRenderMode 或 InteractiveAutoRenderMode。
在组件实例上
<MyChart @rendermode="new InteractiveServerRenderMode(prerender: false)" />
在整个应用中
在 App.razor 中:
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
注意:父级的预渲染设置会覆盖子级设置。如果 <Routes> 禁用了预渲染,各个页面就无法重新启用预渲染。
将页面排除在交互式路由之外
在全局交互式应用中,某些页面可能需要 HttpContext(Cookie、请求标头、响应状态码)。这些页面必须通过静态 SSR 渲染,而不能在交互式运行时中渲染。
使用 [ExcludeFromInteractiveRouting]:
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]
<h1>Privacy Policy</h1>
导航到此页面时会强制执行完整页面重新加载,退出交互式路由。页面会以静态 SSR 渲染,并可完整访问 HttpContext。
在 App.razor 中有条件地应用渲染模式:
<!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;
}
将 InteractiveServer 替换为应用配置的渲染模式。
在运行时检测预渲染与交互式状态
使用 RendererInfo,对仅应在交互模式下运行的代码进行保护:
protected override async Task OnInitializedAsync()
{
if (RendererInfo.IsInteractive)
{
// Only runs during the interactive render, not during prerender
await StartSignalRConnection();
}
}
RendererInfo 属性:
IsInteractive— 预渲染期间为false,交互式运行时接入后为trueName— 预渲染期间为"Static",交互模式下为"Server"或"WebAssembly"
客户端服务在预渲染期间不可用
.Client 项目中的组件会在服务器上预渲染。仅在客户端 Program.cs 中注册的服务(例如 IWebAssemblyHostEnvironment)在预渲染期间不可用。
可通过以下任一方式解决:
- 在服务器上注册匹配的服务 — 两个
Program.cs文件都提供该服务 - 将服务设为可选 — 使用带有可空默认值的构造函数注入:
public MyComponent(IMyService? svc = null) - 创建服务抽象 — 在
.Client中定义接口,并在两个项目中分别实现 - 禁用该组件的预渲染
不要做的事
- 不要在
OnInitializedAsync中调用 JS 互操作——预渲染期间无法使用 JS。请使用OnAfterRenderAsync(firstRender)。 - 不要假设
OnInitializedAsync只运行一次——启用预渲染时它会运行两次。请始终使用[PersistentState]或??=作为保护。 - 不要在交互式组件中使用
HttpContext——它只在静态预渲染期间可用,在交互式生命周期中不可用。需要使用它的页面请使用[ExcludeFromInteractiveRouting]。 - 不要把禁用预渲染作为首选方案——这会损害感知加载速度和 SEO。请改用
[PersistentState]保留状态。