mcpField
S
support-prerendering

support-prerendering

github.com/dotnet开发5.8k3.1k 次安装1 天前更新
代码仓库

关于 support-prerendering

面向交互式 Blazor 应用开发者的预渲染技能,涵盖状态保留、按需关闭预渲染,以及处理依赖交互运行时的代码。

主要功能

  1. 01预渲染生命周期说明
  2. 02跨渲染状态持久化
  3. 03按需控制预渲染
  4. 04为 HttpContext 使用静态路由
  5. 05运行时交互状态检测
  6. 06预渲染时配置客户端服务

适用场景

  1. 01避免重复获取数据
  2. 02在渲染后使用浏览器 API
  3. 03提供需要 HttpContext 的页面
  4. 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,交互式运行时接入后为 true
  • Name — 预渲染期间为 "Static",交互模式下为 "Server" 或 "WebAssembly"

客户端服务在预渲染期间不可用

.Client 项目中的组件会在服务器上预渲染。仅在客户端 Program.cs 中注册的服务(例如 IWebAssemblyHostEnvironment)在预渲染期间不可用。

可通过以下任一方式解决:

  1. 在服务器上注册匹配的服务 — 两个 Program.cs 文件都提供该服务
  2. 将服务设为可选 — 使用带有可空默认值的构造函数注入:public MyComponent(IMyService? svc = null)
  3. 创建服务抽象 — 在 .Client 中定义接口,并在两个项目中分别实现
  4. 禁用该组件的预渲染

不要做的事

  • 不要在 OnInitializedAsync 中调用 JS 互操作——预渲染期间无法使用 JS。请使用 OnAfterRenderAsync(firstRender)。
  • 不要假设 OnInitializedAsync 只运行一次——启用预渲染时它会运行两次。请始终使用 [PersistentState] 或 ??= 作为保护。
  • 不要在交互式组件中使用 HttpContext——它只在静态预渲染期间可用,在交互式生命周期中不可用。需要使用它的页面请使用 [ExcludeFromInteractiveRouting]。
  • 不要把禁用预渲染作为首选方案——这会损害感知加载速度和 SEO。请改用 [PersistentState] 保留状态。

评论

支持 Markdown