ASP.NET Core Blazor 呈现模式
注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
本文介绍在编译时或运行时控制 Blazor Web App 中的 Razor 组件呈现。
本指南不适用于独立的 Blazor WebAssembly 应用。 Blazor WebAssembly 应用仅通过基于客户端 WebAssembly 的运行时在客户端上呈现,并且没有呈现模式的概念。 如果呈现模式应用于 Blazor WebAssembly 应用中的组件,则呈现模式指定对呈现组件没有任何影响。
呈现模式
Blazor Web App 中的每个组件会采用呈现模式来确定其使用的托管模型、呈现位置以及它是否是交互式的。
下表显示了用于呈现 Blazor Web App 中 Razor 组件的可用呈现模式。 要将呈现模式应用于组件,请对组件实例或组件定义使用 @rendermode
指令。 本文稍后将介绍每个呈现模式方案的示例。
名称 | 描述 | 呈现位置 | 交互 |
---|---|---|---|
静态服务器 | 静态服务器端呈现(静态 SSR) | 服务器 | 否 |
交互式服务器 | 使用 Blazor Server 的交互式服务器端呈现(交互式 SSR)。 | 服务器 | 是 |
交互式 WebAssembly | 使用 Blazor WebAssembly 的客户端呈现 (CSR)†。 | 客户端 | 是 |
交互式自动 | 下载 Blazor 捆绑包后,在后续访问时先使用 Blazor Server 然后使用 CSR 的交互式 SSR。 | 服务器,然后客户端 | 是 |
†客户端呈现 (CSR) 假定为交互式。 行业或 Blazor 文档中不使用“交互式客户端呈现”和“交互式 CSR”。
默认情况下,交互式组件会启用预呈现。 本文稍后部分提供了有关控制预呈现的指导。 有关客户端和服务器呈现概念的一般行业术语,请参阅 ASP.NET Core Blazor 基础知识。
以下示例演示了如何使用一些基本 Razor 组件功能设置组件的呈现模式。
要在本地测试呈现模式行为,可以将以下组件放置在根据 Blazor Web App 项目模板创建的应用中。 创建应用时,请从下拉菜单中选择选项 (Visual Studio) 或应用 CLI 选项 (.NET CLI) 来同时启用服务器端和客户端交互。 有关如何创建 Blazor Web App 的指导,请参阅适用于 ASP.NET Core Blazor 的工具。
启用对交互式呈现模式的支持
Blazor Web App 必须配置为支持交互式呈现模式。 以下扩展会在创建应用期间自动应用于根据 Blazor Web App 项目模板创建的应用。 在应用的 Program
文件中配置组件服务和终结点后,各个组件仍需根据呈现模式部分声明其呈现模式。
通过调用 AddRazorComponents 添加 Razor 组件的服务。
组件生成器扩展:
- AddInteractiveServerComponents 添加服务以支持呈现交互式服务器组件。
- AddInteractiveWebAssemblyComponents 添加服务以支持呈现交互式 WebAssembly 组件。
MapRazorComponents 发现可用的组件并指定应用的根组件(加载的第一个组件);默认情况下,该组件是 App
组件 (App.razor
)。
终结点约定生成器扩展:
- AddInteractiveServerRenderMode 为应用配置交互式服务器端呈现(交互式 SSR)。
- AddInteractiveWebAssemblyRenderMode 为应用配置交互式 WebAssembly 呈现模式。
注意
有关 API 在以下示例中放置的方向,请检查根据 Blazor Web App 项目模板生成的应用的 Program
文件。 有关如何创建 Blazor Web App 的指导,请参阅适用于 ASP.NET Core Blazor 的工具。
示例 1:以下 Program
文件 API 添加了用于启用交互式 SSR 的服务和配置:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
示例 2:以下 Program
文件 API 添加了用于启用交互式 WebAssembly 呈现模式的服务和配置:
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();
app.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode();
示例 3:以下 Program
文件 API 添加了用于启用交互式服务器、交互式 WebAssembly 和交互式自动呈现模式的服务和配置:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode();
Blazor 使用 Blazor WebAssembly 托管模型下载和执行使用交互式 WebAssembly 呈现模式的组件。 需要单独的客户端项目来为这些组件设置 Blazor WebAssembly 托管。 客户端项目包含 Blazor WebAssembly 主机的启动代码,并设置 .NET 运行时以在浏览器中运行。 当选择启用 WebAssembly 交互的选项时,Blazor Web App 模板会为你添加此客户端项目。 应从客户端项目生成任何使用交互式 WebAssembly 呈现模式的组件,以便它们包含在下载的应用捆绑包中。
将呈现模式应用于组件实例
要将呈现模式应用于组件实例,可利用使用组件的 @rendermode
Razor 指令属性。
在以下示例中,交互式服务器端呈现(交互式 SSR)应用于 Dialog
组件实例:
<Dialog @rendermode="InteractiveServer" />
注意
Blazor 模板在应用 _Imports
文件中包括一个 RenderMode 的静态 using
指令(Components/_Imports.razor
),用于更短的 @rendermode
语法:
@using static Microsoft.AspNetCore.Components.Web.RenderMode
如果没有前面的指令,组件必须在 @rendermode
语法中指定静态 RenderMode 类:
<Dialog @rendermode="RenderMode.InteractiveServer" />
还可以引用直接使用自定义配置实例化的自定义呈现模式实例。 有关详细信息,请参阅本文后面的自定义速记呈现模式部分。
将呈现模式应用于组件定义
要将组件的呈现模式指定为其定义的一部分,请使用 @rendermode
Razor 指令和相应的呈现模式属性。
@page "..."
@rendermode InteractiveServer
将呈现模式应用于特定页面时,通常会将呈现模式应用于组件定义。 可路由页面会使用与呈现页面的 Router 组件相同的呈现模式。
从技术上说,@rendermode
既是 Razor 指令,也是 Razor 指令属性。 语义相似,但存在差异。 @rendermode
指令位于组件定义上,因此引用的呈现模式实例必须是静态的。 @rendermode
指令属性可以采用任何呈现模式实例。
注意
组件作者应避免将组件的实现与特定呈现模式耦合。 相反,组件作者通常应将组件设计为支持任何呈现模式或托管模型。 组件的实现应避免假设其运行位置(服务器或客户端),并在静态呈现时正常降级。 如果未直接实例化组件(例如使用可路由页面组件),或者为所有组件实例指定呈现模式,则可能需要在组件定义中指定呈现模式。
将呈现模式应用于整个应用
若要为整个应用设置呈现模式,请在应用组件层次结构中不是根组件的最高级交互组件上指示呈现模式。
注意
不支持让根组件具有交互性(例如 App
组件)。 因此,App
组件无法直接设置整个应用的呈现模式。
对于基于 Blazor Web App 项目模板的应用,通常将指定一个分配给整个应用的呈现模式,其中的 Routes
组件在 App
组件中使用 (Components/App.razor
):
<Routes @rendermode="InteractiveServer" />
Router 组件会将其呈现模式传播到它路由的页面。
通常还必须在 HeadOutlet
组件上设置相同的交互式呈现模式,该模式也可以从项目模板生成的 Blazor Web App 应用的 App
组件中找到:
<HeadOutlet @rendermode="InteractiveServer" />
如果应用采用交互式客户端(WebAssembly 或自动)呈现模式,并通过 Routes
组件为整个应用启用呈现模式:
- 将服务器应用的
Components/Layout
文件夹的布局和导航文件放置或移动到.Client
项目的Layout
文件夹中。 在.Client
项目中创建一个Layout
文件夹(如果不存在)。 - 将服务器应用的
Components/Pages
文件夹的组件放置或移动到.Client
项目的Pages
文件夹中。 在.Client
项目中创建一个Pages
文件夹(如果不存在)。 - 将服务器应用的
Components
文件夹的Routes
组件放置或移动到.Client
项目的根文件夹中。
若要在创建 Blazor Web App 时启用全局交互性:
- Visual Studio:将“交互位置”下拉列表设置为“全局”。
- .NET CLI:使用
-ai|--all-interactive
选项。
有关详细信息,请参阅用于 ASP.NET Core Blazor 的工具。
以编程方式应用呈现模式
属性和字段可以分配呈现模式。
本部分所述的第二种方法(按组件实例设置渲染模式)在应用规范要求一个或多个组件在全局交互式应用中采用静态 SSR 时特别有用。 本文后面的全局交互式应用中的静态 SSR 页面介绍了此方案。
按组件定义设置呈现模式
组件定义可以通过专用字段定义呈现模式:
@rendermode pageRenderMode
...
@code {
private static IComponentRenderMode pageRenderMode = InteractiveServer;
}
按组件实例设置呈现模式
以下示例将交互式服务器端呈现(交互式 SSR)应用于任何请求。
<Routes @rendermode="PageRenderMode" />
...
@code {
private IComponentRenderMode? PageRenderMode => InteractiveServer;
}
本文后面的呈现模式传播部分提供了有关呈现模式传播的其他信息。 全局交互式应用中的静态 SSR 页面部分演示如何使用上述方法在全局交互式应用中采用静态 SSR。
在运行时检测呈现位置、交互性和分配的呈现模式
ComponentBase.RendererInfo 和 ComponentBase.AssignedRenderMode 属性允许应用检测组件的位置、交互性和分配的呈现模式的详细信息:
- RendererInfo.Name 返回组件正在执行的位置:
Static
:在服务器上 (SSR),无法交互。Server
:在服务器上 (SSR),能够在预呈现后交互。WebAssembly
:在客户端上 (CSR),能够在预呈现后交互。WebView
:在本机设备上,能够在预呈现后交互。
- RendererInfo.IsInteractive 指示组件在呈现时是否支持交互。 交互式呈现时为值
true
,预呈现或静态 SSR (RendererInfo.Name/Static
) 时值为false
。 - ComponentBase.AssignedRenderMode 公开组件的已分配呈现模式:
InteractiveServer
表示交互式服务器。InteractiveAuto
表示交互式自动。InteractiveWebAssembly
表示交互式 WebAssembly。
组件使用这些属性根据内容的位置或交互状态来呈现内容。 以下示例演示典型用例。
在组件是交互式组件之前显示内容:
@if (!RendererInfo.IsInteractive)
{
<p>Connecting to the assistant...</p>
}
else
{
...
}
在组件是交互式组件之前禁用按钮:
<button @onclick="Send" disabled="@(!RendererInfo.IsInteractive)">
Send
</button>
在预呈现期间禁用窗体,并在组件交互时启用窗体:
<EditForm Model="Movie" ...>
<fieldset disabled="@disabled">
...
<button type="submit" >Save</button>
</fieldset>
</EditForm>
@code {
private bool disabled = true;
[SupplyParameterFromForm]
private Movie? Movie { get; set; }
protected override async Task OnInitializedAsync()
{
Movie ??= await ...;
if (RendererInfo.IsInteractive)
{
disabled = false;
}
}
}
如果静态呈现组件,则呈现标记以支持执行常规 HTML 操作:
@if (AssignedRenderMode is null)
{
// The render mode is Static Server
<form action="/movies">
<input type="text" name="titleFilter" />
<input type="submit" value="Search" />
</form>
}
else
{
// The render mode is Interactive Server, WebAssembly, or Auto
<input @bind="titleFilter" />
<button @onclick="FilterMovies">Search</button>
}
在上面的示例中:
- 当 AssignedRenderMode 的值为
null
时,组件将采用静态 SSR。 Blazor 事件处理在具有静态 SSR 的浏览器中不起作用,因此组件会提交窗体(GET 请求),并将titleFilter
查询字符串设置为用户的<input>
值。Movie
组件 (/movie
) 可以读取查询字符串并处理titleFilter
的值,以使用筛选的结果呈现组件。 - 否则,呈现模式为
InteractiveServer
、InteractiveWebAssembly
或InteractiveAuto
中的任一个。 该组件能够使用事件处理程序委托 (FilterMovies
) 以及绑定到<input>
元素 (titleFilter
) 的值在后台 SignalR 连接上以交互方式筛选电影。
Blazor Web App 的 Blazor 文档示例
使用 Blazor Web App 时,大多数 Blazor 文档示例组件需要交互性才能运行并演示文章中涵盖的概念。 测试某篇文章提供的示例组件时,请确保应用采用全局交互性或组件采用交互式呈现模式。
预呈现
预呈现是最初在服务器上呈现页面内容的过程,而无需为呈现的控件启用事件处理程序。 服务器会根据初始请求尽快输出页面的 HTML UI,这会让应用感觉对用户的响应更强。 预呈现还可以通过呈现搜索引擎可用来计算网页排名的初始 HTTP 响应的内容,来改进搜索引擎优化 (SEO)。
默认情况下,交互式组件会启用预呈现。
交互式路由的内部导航不涉及从服务器请求新页面内容。 因此,不会对内部页面请求(包括增强的导航)进行预渲染。 有关详细信息,请参阅静态路由与交互式路由、交互式路由和预渲染以及增强的导航和窗体处理。
使用以下技术禁用预渲染的操作仅对顶级渲染模式有效。 如果父组件指定了渲染模式,则会忽略其子级的预渲染设置。 我们正在调查此行为,以确定是否可以在 2025 年 11 月发布的 .NET 10 版本中做出相应更改。
若要禁用组件实例的预呈现,请向呈现模式传递值为 false
的 prerender
标志:
<... @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<... @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
<... @rendermode="new InteractiveAutoRenderMode(prerender: false)" />
若要在组件定义中禁用预呈现:
@rendermode @(new InteractiveServerRenderMode(prerender: false))
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
@rendermode @(new InteractiveAutoRenderMode(prerender: false))
若要禁用整个应用的呈现模式,请在应用组件层次结构中不是根组件的最高级交互组件上指示呈现模式。
对于基于 Blazor Web App 项目模板的应用,将指定一个分配给整个应用的呈现模式,其中的 Routes
组件在 App
组件中使用 (Components/App.razor
)。 以下示例将应用的呈现模式设置为禁用预呈现的交互服务器:
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
此外,请为 App
组件中的 HeadOutlet
组件禁用预呈现:
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
不支持使根组件(如 App
组件)与根组件定义文件 (.razor
) 顶部的 @rendermode
指令交互。 因此,App
组件无法直接禁用预呈现。
静态服务器端呈现(静态 SSR)
组件使用静态服务器端呈现(静态 SSR)。 未启用组件呈现到响应流和交互性。
以下示例没有指定组件呈现模式,并且组件会从其父级继承其呈现模式。 由于没有上级组件指定呈现模式,因此会在服务器上静态呈现以下组件。 按钮不是交互式的,并且选择按钮后不会调用 UpdateMessage
方法。 message
值不会更改,并且不会在响应 UI 事件时重新呈现组件。
RenderMode1.razor
:
@page "/render-mode-1"
<button @onclick="UpdateMessage">Click me</button> @message
@code {
private string message = "Not updated yet.";
private void UpdateMessage()
{
message = "Somebody updated me!";
}
}
如果在 Blazor Web App 中本地使用上述组件,请将该组件放置在服务器项目的 Components/Pages
文件夹中。 服务器项目是解决方案的项目,其名称不以 .Client
结尾。 当应用运行时,在浏览器的地址栏中导航到 /render-mode-1
。
在静态 SSR 期间,Razor 组件页请求由服务器端 ASP.NET Core 中间件管道请求处理进行路由和授权。 路由和授权的专用 Blazor 功能不起作用,因为在服务器端请求处理期间不会呈现 Razor 组件。 在静态 SSR 期间不可用的 Routes
组件中的 Blazor 路由器功能包括显示:
未经授权的内容 (
<NotAuthorized>...</NotAuthorized>
) (NotAuthorized):Blazor Web App 通常通过自定义授权中间件的行为来处理服务器上的未经授权的请求。找不到内容 (
<NotFound>...</NotFound>
) (NotFound):Blazor Web App 通常通过显示浏览器的内置 404 UI 或通过 ASP.NET Core 中间件返回自定义 404 页面(或其他响应)来处理服务器上的错误 URL 请求(例如UseStatusCodePagesWithRedirects
/ API 文档)。
如果应用表现出根级交互性,则在初始静态 SSR 之后不涉及服务器端 ASP.NET Core 请求处理,这意味着上述 Blazor 功能可以按预期工作。
使用静态 SSR 的增强型导航时,在加载 JavaScript 时需要特别注意。 有关详细信息,请参阅 ASP.NET Core Blazor JavaScript 及静态服务器端呈现(静态 SSR)。
交互式服务器端呈现(交互式 SSR)
交互式服务器端呈现(交互式 SSR)使用 Blazor Server 从服务器以交互方式呈现组件。 用户交互通过与浏览器的实时连接进行处理。 在呈现服务器组件时,将会建立线路连接。
在以下示例中,呈现模式是通过将 @rendermode InteractiveServer
添加到组件定义来设置为“交互式 SSR”的。 选择按钮后,将会调用 UpdateMessage
方法。 值 message
会发生更改,并重新呈现组件以更新 UI 中的消息。
RenderMode2.razor
:
@page "/render-mode-2"
@rendermode InteractiveServer
<button @onclick="UpdateMessage">Click me</button> @message
@code {
private string message = "Not updated yet.";
private void UpdateMessage()
{
message = "Somebody updated me!";
}
}
如果在某个 Blazor Web App中使用上述组件,请将该组件放置在服务器项目的 Components/Pages
文件夹中。 服务器项目是解决方案的项目,其名称不以 .Client
结尾。 当应用运行时,在浏览器的地址栏中导航到 /render-mode-2
。
客户端呈现 (CSR)
客户端呈现(CSR)使用 Blazor WebAssembly 以交互方式在客户端上呈现组件。 最初呈现 WebAssembly 组件时,将会下载并缓存 .NET 运行时和应用捆绑包。 必须从设置 Blazor WebAssembly 主机的单独客户端项目中生成使用 CSR 的组件。
在下面的示例中,呈现模式是通过 @rendermode InteractiveWebAssembly
设置为 CSR 的。 选择按钮后,将会调用 UpdateMessage
方法。 值 message
会发生更改,并重新呈现组件以更新 UI 中的消息。
RenderMode3.razor
:
@page "/render-mode-3"
@rendermode InteractiveWebAssembly
<button @onclick="UpdateMessage">Click me</button> @message
@code {
private string message = "Not updated yet.";
private void UpdateMessage()
{
message = "Somebody updated me!";
}
}
如果在 Blazor Web App 中本地使用上述组件,请将该组件放置在客户端项目的 Pages
文件夹中。 客户端项目是解决方案的项目,其名称以 .Client
结尾。 当应用运行时,在浏览器的地址栏中导航到 /render-mode-3
。
自动 (Auto) 呈现
自动 (Auto) 呈现会决定如何在运行时呈现组件。 该组件最初使用 Blazor Server 托管模型通过交互式服务器端呈现(交互式 SSR)进行呈现。 .NET 运行时和应用捆绑包会在后台下载到客户端并缓存,以便将来访问时可以使用它们。
自动呈现模式绝不会动态更改页面上已存在组件的呈现模式。 自动呈现模式对组件使用哪种类型的交互性做出初始决定,之后只要组件位于页面上,就会保留该类型的交互性。 此初始决定的一个因素是考虑页面上是否已存在具有 WebAssembly/服务器交互性的组件。 自动模式倾向于选择与现有交互式组件的呈现模式一致的呈现模式。 自动模式倾向于使用现有交互性模式的原因是避免引入不与现有运行时共享状态的新交互式运行时。
必须从设置 Blazor WebAssembly 主机的单独客户端项目中生成使用自动呈现模式的组件。
在以下示例中,组件在整个过程中都是交互式的。 选择按钮后,将会调用 UpdateMessage
方法。 值 message
会发生更改,并重新呈现组件以更新 UI 中的消息。 最初,组件是从服务器以交互方式呈现的,但在后续访问时,会在下载并缓存 .NET 运行时和应用捆绑包后从客户端呈现该组件。
RenderMode4.razor
:
@page "/render-mode-4"
@rendermode InteractiveAuto
<button @onclick="UpdateMessage">Click me</button> @message
@code {
private string message = "Not updated yet.";
private void UpdateMessage()
{
message = "Somebody updated me!";
}
}
如果在 Blazor Web App 中本地使用上述组件,请将该组件放置在客户端项目的 Pages
文件夹中。 客户端项目是解决方案的项目,其名称以 .Client
结尾。 当应用运行时,在浏览器的地址栏中导航到 /render-mode-4
。
呈现模式传播
呈现模式会向下传播组件层次结构。
应用呈现模式的规则:
- 默认呈现模式为静态。
- 交互式服务器 (InteractiveServer)、交互式 WebAssembly (InteractiveWebAssembly) 和交互式自动 (InteractiveAuto) 呈现模式可从组件使用,包括对同级组件使用不同的呈现模式。
- 无法在子组件中切换到其他交互式呈现模式。 例如,服务器组件不能是 WebAssembly 组件的子组件。
- 从静态父级传递到交互式子组件的参数必须是 JSON 可序列化的。 这意味着无法将呈现片段或子内容从静态父级组件传递到交互式子组件。
以下示例使用了不可路由的非页面 SharedMessage
组件。 与呈现模式无关的 SharedMessage
组件不会使用 @attribute
指令应用呈现模式。 如果要使用 Blazor Web App 测试这些方案,请将以下组件放置在应用的 Components
文件夹中。
SharedMessage.razor
:
<p>@Greeting</p>
<button @onclick="UpdateMessage">Click me</button> @message
<p>@ChildContent</p>
@code {
private string message = "Not updated yet.";
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public string Greeting { get; set; } = "Hello!";
private void UpdateMessage()
{
message = "Somebody updated me!";
}
}
呈现模式继承
如果将 SharedMessage
组件放置在静态呈现的父级组件中,则 SharedMessage
组件也会以静态方式呈现,并且不是交互式的。 该按钮不会调用 UpdateMessage
,并且消息不会更新。
RenderMode5.razor
:
@page "/render-mode-5"
<SharedMessage />
如果将 SharedMessage
组件放置在定义呈现模式的组件中,它将继承已应用的呈现模式。
在以下示例中,SharedMessage
组件通过 SignalR 与客户端的连接进行交互。 该按钮会调用 UpdateMessage
,并且消息将会更新。
RenderMode6.razor
:
@page "/render-mode-6"
@rendermode InteractiveServer
<SharedMessage />
具有不同呈现模式的子组件
在以下示例中,这两个 SharedMessage
组件都是预呈现的,并且会在浏览器中显示页面时出现。
- 建立线路后Blazor,第一个
SharedMessage
具有交互式服务器端呈现(交互式 SSR)的组件是交互式的SignalR。 - 下载了 Blazor 应用捆绑包,并且 .NET 运行时在客户端上处于活动状态之后,使用客户端呈现 (CSR) 的第二个
SharedMessage
组件是交互式的。
RenderMode7.razor
:
@page "/render-mode-7"
<SharedMessage @rendermode="InteractiveServer" />
<SharedMessage @rendermode="InteractiveWebAssembly" />
具有可序列化参数的子组件
以下示例演示了采用参数的交互式子组件。 参数必须可序列化。
RenderMode8.razor
:
@page "/render-mode-8"
<SharedMessage @rendermode="InteractiveServer" Greeting="Welcome!" />
不支持不可序列化的组件参数,例如子内容或呈现片段。 在以下示例中,将子内容传递给 SharedMessage
组件会导致运行时错误。
RenderMode9.razor
:
@page "/render-mode-9"
<SharedMessage @rendermode="InteractiveServer">
Child content
</SharedMessage>
错误:
System.InvalidOperationException:无法使用 rendermode“InteractiveServerRenderMode”将参数“ChildContent”传递给组件“SharedMessage”。 这是因为该参数属于委托类型“Microsoft.AspNetCore.Components.RenderFragment”,这是任意代码,而且无法序列化。
要规避上述限制,请将子组件包装在不包含该参数的另一个组件中。 这是使用 Routes
组件(Components/Routes.razor
)包装 Router 组件的 Blazor Web App 项目模板采用的方法。
WrapperComponent.razor
:
<SharedMessage>
Child content
</SharedMessage>
RenderMode10.razor
:
@page "/render-mode-10"
<WrapperComponent @rendermode="InteractiveServer" />
在上面的示例中:
- 子内容将会传递到
SharedMessage
组件,而不会生成运行时错误。 - 组件
SharedMessage
会以交互方式呈现在服务器上。
呈现模式与其父级不同的子组件
请勿尝试对子组件应用与其父级组件不同的交互式呈现模式。
呈现组件时,以下组件会导致运行时错误:
RenderMode11.razor
:
@page "/render-mode-11"
@rendermode InteractiveServer
<SharedMessage @rendermode="InteractiveWebAssembly" />
错误:
Cannot create a component of type 'BlazorSample.Components.SharedMessage' because its render mode 'Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode' is not supported by Interactive Server rendering.
全局交互式应用中的静态 SSR 页面
在某些情况下,应用的规范要求组件采用静态服务器端呈现(静态 SSR),并且仅在服务器上运行,而应用的 rest 则使用交互式呈现模式。
仅当应用的特定页面无法使用交互式 Server 或 WebAssembly 渲染时,此方法才有用。 例如,对于依赖于读/写 HTTP Cookie 并且只能在请求/响应周期中运行而无法通过交互式呈现加载的页面,可采用这种方法。 对于使用交互式渲染的页面,不应强制它们使用静态 SSR 渲染,因为这一方法的效率较低且对最终用户的响应较差。
使用指令分配@attribute
的属性标记任何Razor组件页[ExcludeFromInteractiveRouting]
:Razor
@attribute [ExcludeFromInteractiveRouting]
应用该属性导致访问页面从而退出交互式路由。 入站导航会强制执行全页重载,而不是通过交互式路由解析页面。 全页重载会迫使顶级根组件从服务器重新渲染,通常情况下,这一组件是 App
组件 (App.razor
),重新渲染可以让应用切换到其他顶级渲染模式。
扩展RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting方法允许组件检测属性是否[ExcludeFromInteractiveRouting]
应用于当前页。
在 App
组件中,使用以下示例中的模式:
- 未使用属性注释的页面
[ExcludeFromInteractiveRouting]
默认为InteractiveServer
具有全局交互性的呈现模式。 可以将InteractiveServer
替换为InteractiveWebAssembly
或InteractiveAuto
以指定不同的默认全局渲染模式。 - 使用
[ExcludeFromInteractiveRouting]
属性 进行批注的页面采用静态 SSR(PageRenderMode
已null
分配)。
<!DOCTYPE html>
<html>
<head>
...
<HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
<Routes @rendermode="@PageRenderMode" />
...
</body>
</html>
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? PageRenderMode
=> HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}
使用 RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting 扩展方法的替代方法是使用 HttpContext.GetEndpoint()?.Metadata
手动读取终结点元数据。
可通过两种方法实现对呈现模式的精细控制,以下小节中介绍了每种方法:
静态 SSR 组件的区域(文件夹):应用的一个区域(文件夹)中的组件必须采用静态 SSR,并共用相同的路由路径前缀。 应用通过基于文件夹路径在
App
组件中的Routes
组件上设置呈现模式,全局控制呈现模式。分布在整个应用中的静态 SSR 组件:分布在应用中不同位置的组件必须采用静态 SSR 且仅在服务器上运行。 纯静态 SSR 组件不在单个文件夹中,也不共享共同的路由路径前缀。 应用通过在组件实例中使用
@rendermode
指令设置呈现模式,基于每个组件控制呈现模式。 在App
组件中使用反射来设置Routes
组件上的呈现模式。
在这两种情况下,必须采用静态 SSR 的组件还必须强制重新加载整页。
以下示例使用 HttpContext 级联参数来确定页面是否以静态方式呈现。 null
HttpContext 指示组件以交互方式呈现,可作为应用代码中的信号触发全页面重新加载。
静态 SSR 组件的区域(文件夹)
此小节中所述的方法由 Blazor Web App 项目模板使用,该模板中包含个人身份验证和全局交互性。
应用的区域(文件夹)包含必须采用静态 SSR 且仅在服务器上运行的组件。 文件夹中的组件共用相同的路由路径前缀。 例如,Blazor Web App 项目模板的 IdentityRazor 组件位于 Components/Account/Pages
文件夹中,共用根路径前缀 /Account
。
该文件夹还包含一个 _Imports.razor
文件,该文件将自定义帐户布局应用于文件夹中的组件:
@using BlazorSample.Components.Account.Shared
@layout AccountLayout
Shared
文件夹维护 AccountLayout
布局组件。 该组件利用 HttpContext 来确定组件是否采用了静态 SSR。 Identity 组件必须使用静态 SSR 在服务器上呈现,因为它们设置了 Identity Cookie。 如果 HttpContext 的值为 null
,则组件以交互方式呈现,并通过调用 NavigationManager.Refresh 和将 forceLoad
设置为 true
来执行全页面重新加载。 这会强制使用静态 SSR 重新呈现页面。
Components/Account/Shared/AccountLayout.razor
:
@inherits LayoutComponentBase
@layout BlazorSample.Components.Layout.MainLayout
@inject NavigationManager Navigation
@if (HttpContext is null)
{
<p>Loading...</p>
}
else
{
@Body
}
@code {
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
protected override void OnParametersSet()
{
if (HttpContext is null)
{
Navigation.Refresh(forceReload: true);
}
}
}
注意
在 Blazor Web App 项目模板中,Components/Account/Pages/Manage
文件夹中的 Identity 组件有第二个布局文件(Components/Account/Shared
文件夹中的 ManageLayout.razor
)。 Manage
文件夹有其自己的 _Imports.razor
文件,用于将 ManageLayout
应用到文件夹中的组件。 在自己的应用中,使用嵌套 _Imports.razor
文件是将自定义布局应用于页面组的有用方法。
在 App
组件中,对 Account
文件夹中组件的任何请求都会应用 null
呈现模式,该模式会强制使用静态 SSR。 其他组件请求接收交互式 SSR 呈现模式(InteractiveServer
)的全局应用。
重要
应用 null
呈现模式并不总是强制采用静态 SSR。 只是在使用本节所示的方法时,它恰好采用此行为。
null
呈现模式实际上等同于未指定呈现模式,这会导致组件继承其父级的呈现模式。 在这种情况下,App
组件使用静态 SSR 呈现,因此 null
呈现模式会导致 Routes
组件从 App
组件继承静态 SSR。 如果为父组件使用交互式呈现模式的子组件指定了 null 呈现模式,则子组件会继承相同的交互式呈现模式。
Components/App.razor
:
<Routes @rendermode="RenderModeForPage" />
...
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? RenderModeForPage =>
HttpContext.Request.Path.StartsWithSegments("/Account")
? null
: {INTERACTIVE RENDER MODE};
}
在前面的代码中,将 {INTERACTIVE RENDER MODE}
占位符更改为适当的值,具体取决于应用程序的 rest 是否应采用全局 InteractiveServer、InteractiveWebAssembly 或 InteractiveAuto 呈现。
不需要必须在 Account
文件夹中采用静态 SSR 的组件来设置布局,该布局通过 _Imports.razor
文件应用。 组件不会设置呈现模式,因为它们应使用静态 SSR 来呈现。 对于 Account
文件夹中的组件,无需执行任何其他操作即可强制采用静态 SSR。
分布在整个应用中的静态 SSR 组件
在前面的小节中,应用通过在 App
组件中全局设置呈现模式来控制组件的呈现模式。 或者,App
组件还可以采用“每组件”呈现模式来设置呈现模式,从而允许应用中分布的组件强制采用静态 SSR。 本小节介绍该方法。
应用具有一个自定义布局,可应用于应用中分布的组件。 通常,应用的共享组件放置在 Components/Layout
文件夹中。 该组件利用 HttpContext 来确定组件是否采用了静态 SSR。 如果 HttpContext 的值为 null
,则组件以交互方式呈现,并通过调用 NavigationManager.Refresh 和将 forceLoad
设置为 true
来执行全页面重新加载。 这会触发向服务器发出针对组件的请求。
Components/Layout/StaticSsrLayout.razor
:
@inherits LayoutComponentBase
@layout MainLayout
@inject NavigationManager Navigation
@if (HttpContext is null)
{
<p>Loading...</p>
}
else
{
@Body
}
@code {
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
protected override void OnParametersSet()
{
if (HttpContext is null)
{
Navigation.Refresh(forceReload: true);
}
}
}
在 App
组件中,反射用于设置呈现模式。 向单个组件定义文件分配的任何呈现模式都会应用于 Routes
组件。
Components/App.razor
:
<Routes @rendermode="RenderModeForPage" />
...
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? RenderModeForPage =>
HttpContext.GetEndpoint()?.Metadata.GetMetadata<RenderModeAttribute>()?
.Mode;
}
每个必须采用静态 SSR 的组件都会设置自定义布局,并且不指定呈现模式。 不指定呈现模式会导致 App
组件中的 null
值为 RenderModeAttribute.Mode,因而不会向 Routes
组件实例分配呈现模式,并强制采用静态 SSR。
重要
应用 null
呈现模式并不总是强制采用静态 SSR。 只是在使用本节所示的方法时,它恰好采用此行为。
null
呈现模式实际上等同于未指定呈现模式,这会导致组件继承其父级的呈现模式。 在这种情况下,App
组件使用静态 SSR 呈现,因此 null
呈现模式会导致 Routes
组件从 App
组件继承静态 SSR。 如果为父组件使用交互式呈现模式的子组件指定了 null 呈现模式,则子组件会继承相同的交互式呈现模式。
除应用自定义布局而不设置交互式呈现模式之外,组件无需执行任何其他操作即可强制执行静态 SSR:
@layout BlazorSample.Components.Layout.StaticSsrLayout
应用周围的交互式组件会避免应用自定义静态 SSR 布局,而仅设置适当的交互式呈现模式,该模式在 App
组件中反射后应用于 Routes
组件:
@rendermode {INTERACTIVE RENDER MODE}
在前面的代码中,将 {INTERACTIVE RENDER MODE}
占位符更改为适当的值,具体取决于组件是否应采用 InteractiveServer、InteractiveWebAssembly 或 InteractiveAuto 呈现。
客户端服务在预呈现期间无法解析
假设未为组件或应用禁用预呈现,则 .Client
项目中的组件在服务器上预呈现。 由于服务器无权访问已注册的客户端 Blazor 服务,因此无法将这些服务注入组件,而不会收到“在预呈现期间找不到该服务”的错误。
例如,请考虑 Blazor Web App 中具有全局交互式 WebAssembly 或交互式自动呈现的 .Client
项目中的以下 Home
组件。 组件尝试注入 IWebAssemblyHostEnvironment 以获取环境的名称。
@page "/"
@inject IWebAssemblyHostEnvironment Environment
<PageTitle>Home</PageTitle>
<h1>Home</h1>
<p>
Environment: @Environment.Environment
</p>
未发生编译时错误,但在预呈现期间发生运行时错误:
无法为“Sample.Client.Pages.Home”类型上的属性“Environment”提供值。 没有“Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment”类型的已注册服务。
发生此错误的原因是组件必须在预呈现期间在服务器上编译和执行,但 IWebAssemblyHostEnvironment 不是服务器上的已注册服务。
如果应用在预呈现期间不需要该值,可以通过注入 IServiceProvider 以获取服务而不是服务类型本身,来解决此问题:
@page "/"
@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
@inject IServiceProvider Services
<PageTitle>Home</PageTitle>
<h1>Home</h1>
<p>
<b>Environment:</b> @environmentName
</p>
@code {
private string? environmentName;
protected override void OnInitialized()
{
if (Services.GetService<IWebAssemblyHostEnvironment>() is { } env)
{
environmentName = env.Environment;
}
}
}
但是,如果逻辑在预呈现过程中需要值,上述方法将不起作用。
如果为组件禁用预呈现,也可以避免此问题,但那是在可能不符合组件规范的许多情况下采取的极端措施。
可以通过三种方法来处理此方案。 下面列出了从最推荐到最不推荐的方案:
建议用于共享框架服务:对于仅未在主项目中服务器端注册的共享框架服务,请在主项目中注册服务,以便在预呈现期间提供这些服务。 有关此方案的示例,请参阅从 ASP.NET Core Blazor 应用调用 Web API 中 HttpClient 服务的指导。
建议用于共享框架之外的服务:为服务器上的服务创建自定义服务实现。 在
.Client
项目的交互式组件中通常使用该服务。 有关此方法的演示,请参阅 ASP.NET Core Blazor 环境。创建服务抽象,并在
.Client
和服务器项目中为服务创建实现。 在每个项目中注册服务。 在组件中注入自定义服务。在服务器上预呈现时,你可能能够向服务器端包添加
.Client
项目包引用,并回退到使用服务器端 API。
从其他程序集中发现组件
必须将其他程序集披露给 Blazor 框架,以发现被引用项目中的可路由 Razor 组件。 有关详细信息,请参阅 ASP.NET Core Blazor 路由和导航。
当没有剩余的交互式服务器组件时,关闭线路
交互式服务器组件使用与浏览器的实时连接(称为线路)来处理 Web UI 事件。 呈现根交互式服务器组件时,将创建线路及其关联状态。 当页面上没有剩余的交互式服务器组件时,线路将关闭,以释放服务器资源。
自定义速记呈现模式
@rendermode
指令采用一个参数,该参数是 IComponentRenderMode 类型的静态实例。 @rendermode
指令属性可以采用任何呈现模式实例(静态或非静态)。 为了方便起见,Blazor 框架为 RenderMode 静态类提供了一些预定义的呈现模式,但你可以创建自己的呈现模式。
通常,组件使用以下 @rendermode
指令来禁用预呈现:
@rendermode @(new InteractiveServerRenderMode(prerender: false))
但是,请考虑以下示例,它通过应用的 _Imports
文件 (Components/_Imports.razor
) 创建了无需预呈现的速记交互式服务器端呈现模式:
public static IComponentRenderMode InteractiveServerWithoutPrerendering { get; } =
new InteractiveServerRenderMode(prerender: false);
在整个 Components
文件夹的组件中使用速记呈现模式:
@rendermode InteractiveServerWithoutPrerendering
或者,单个组件实例可以通过一个专用字段定义自定义的呈现模式:
@rendermode interactiveServerWithoutPrerendering
...
@code {
private static IComponentRenderMode interactiveServerWithoutPrerendering =
new InteractiveServerRenderMode(prerender: false);
}
目前,速记呈现模式方法可能仅用于降低指定 prerender
标志的详细程度。 如果其他标志可用于交互式呈现,并且你希望创建具有不同标志组合的速记呈现模式,那么将来速记方法可能更有用。
通过顶层导入文件 (_Imports.razor
) 进行服务注入
本部分仅适用于 Blazor Web App。
Components
文件夹中的顶层导入文件 (Components/_Imports.razor
) 将其引用注入到文件夹层次结构中的所有组件,包括 App
组件 (App.razor
)。 App
组件始终静态呈现,即使禁用了页面组件的预呈现也是如此。 因此,通过顶层导入文件注入服务会导致在页面组件中解析该服务的两个实例。
若要解决此问题,请在位于 Pages
文件夹中的新导入文件 (Components/Pages/_Imports.razor
) 中注入服务。 从该位置,服务在页面组件中仅会被解析一次。