其他
Blazor 组件启用预渲染时,修复重复加载、闪烁…
当 Blazor 交互组件默认预渲染并出现 OnInitializedAsync 双执行、UI 闪烁、空引用、状态丢失或客户端服务不可用时,用这些做法支持或禁用预渲染。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 57/100 ×0.15
适用场景
Blazor 组件启用预渲染时,修复重复特定
解决思路
- 读取项目 AGENTS.md,确认 Interactivity Mode 和 Interactivity Scope;若为 None (Static SSR) 则不应用本技能,Server、WebAssembly、Auto 需要处理预渲染。
- 预期预渲染默认对所有交互 render mode 开启:服务器先把组件渲染为静态 HTML 并立即发送到浏览器,然后交互运行时(Server/WebAssembly)加载并重新渲染组件。
- 预期 OnInitializedAsync 会运行两次:预渲染期间一次,交互运行时附加后一次。
- 预期 OnAfterRenderAsync 在预渲染期间不会被调用,只在交互渲染后调用。
- 预期交互页面之间的内部导航(interactive routing)会跳过预渲染;预渲染只在整页加载时发生。
- 跨预渲染持久化状态:对需要在预渲染和交互之间保留的属性使用 [PersistentState] 属性,让其在预渲染时自动序列化,并在交互激活时恢复。
- 在 OnInitializedAsync 中使用 ??= 模式,只有属性未从预渲染状态恢复时才获取数据,例如 Forecasts ??= await ForecastService.GetForecastsAsync();。
- 当同一组件类型在同一页面出现多次时,使用 @key 消除状态歧义,例如 <ItemCard @key="item.Id" />。
- 高级场景(动态键、自定义序列化)注入 PersistentComponentState ApplicationState。
- 在 OnInitializedAsync 中调用 ApplicationState.RegisterOnPersisting(PersistOrders)。
- 使用 ApplicationState.TryTakeFromJson<List<Order>>("orders", out var restored) 尝试恢复;若恢复成功则使用 restored,否则获取数据。
- 实现 PersistOrders,调用 ApplicationState.PersistAsJson("orders", orders)。
- 当组件立即依赖浏览器 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(cookies、请求头、响应状态码)的页面使用 [ExcludeFromInteractiveRouting],强制其以静态 SSR 渲染而非在交互运行时内渲染。
- 在页面中应用 [ExcludeFromInteractiveRouting],例如 @page "/privacy" 和 @attribute [ExcludeFromInteractiveRouting];导航到该页会强制整页重载,退出交互路由,以静态 SSR 渲染并可完整访问 HttpContext。
- 在 App.razor 中条件应用 render mode:<HeadOutlet @rendermode="RenderModeForPage" /> 和 <Routes @rendermode="RenderModeForPage" />。
- 在 App.razor 的 @code 中定义 [CascadingParameter] public HttpContext HttpContext { get; set; } = default!; private IComponentRenderMode? RenderModeForPage => HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;,并将 InteractiveServer 替换为应用配置的 render mode。
- 运行时检测预渲染 vs 交互:使用 RendererInfo 守卫只在交互时运行的代码,例如 if (RendererInfo.IsInteractive) { await StartSignalRConnection(); }。
- 使用 RendererInfo.IsInteractive 判断:预渲染期间为 false,交互运行时附加后为 true;使用 RendererInfo.Name 判断:预渲染期间为 "Static",交互时为 "Server" 或 "WebAssembly"。
- 处理 .Client 项目中的服务在预渲染时不可用:.Client 项目中的组件在服务器预渲染,仅注册在客户端 Program.cs 的服务(如 IWebAssemblyHostEnvironment)在预渲染期间不可用。
- 修复客户端服务不可用:在服务器注册匹配服务,让两个 Program.cs 都提供该服务。
- 修复客户端服务不可用:使服务可选,使用构造函数注入可空默认值,例如 public MyComponent(IMyService? svc = null)。
- 修复客户端服务不可用:创建服务抽象,接口放在 .Client,两个项目都提供实现。
- 修复客户端服务不可用:为该组件禁用预渲染。
- 不要调用 JS interop 在 OnInitializedAsync;预渲染期间 JS 不可用,应改用 OnAfterRenderAsync(firstRender)。
- 不要假设 OnInitializedAsync 只运行一次;预渲染时运行两次,始终使用 [PersistentState] 或 ??= 守卫。
- 不要在交互组件中使用 HttpContext;它只在静态预渲染期间可用,不在交互生命周期内;需要时使用 [ExcludeFromInteractiveRouting]。
- 不要把禁用预渲染作为首选;它会损害感知加载时间和 SEO,优先使用 [PersistentState] 保留状态。
适用边界 · 注意事项
- 不用于选择使用哪个 render mode(见 create-blazor-project)。
- 不用于通用组件编写(见 author-component)。
- 当项目 Interactivity Mode 为 None (Static SSR) 时不适用。