ASP.NET Core Blazor 呈现模式

本文介绍在编译时或运行时控制 Blazor Web 应用中的 Razor 组件呈现。

本指南不适用于独立的 Blazor WebAssembly 应用。 Blazor WebAssembly 应用仅通过基于客户端 WebAssembly 的运行时在客户端上呈现,并且没有呈现模式的概念。 如果呈现模式应用于 Blazor WebAssembly 应用中的组件,则呈现模式指定对呈现组件没有任何影响。

呈现模式

Blazor Web 应用中的每个组件会采用呈现模式来确定其使用的托管模型、呈现位置以及它是否是交互式的。

下表显示了用于呈现 Blazor Web 应用中 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 应用项目模板创建的应用中。 创建应用时,请从下拉菜单中选择选项 (Visual Studio) 或应用 CLI 选项 (.NET CLI) 来同时启用服务器端和客户端交互。 有关如何创建 Blazor Web 应用的指导,请参阅适用于 ASP.NET Core Blazor 的工具

启用对交互式呈现模式的支持

Blazor Web 应用必须配置为支持交互式呈现模式。 以下扩展会在创建应用期间自动应用于根据 Blazor Web 应用项目模板创建的应用。 在应用的 Program 文件中配置组件服务和终结点后,各个组件仍需根据呈现模式部分声明其呈现模式。

通过调用 AddRazorComponents 添加 Razor 组件的服务。

组件生成器扩展:

MapRazorComponents 发现可用的组件并指定应用的根组件(加载的第一个组件);默认情况下,该组件是 App 组件 (App.razor)。

终结点约定生成器扩展:

注意

有关 API 在以下示例中放置的方向,请检查根据 Blazor Web 应用项目模板生成的应用的 Program 文件。 有关如何创建 Blazor Web 应用的指导,请参阅适用于 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 应用模板会为你添加此客户端项目。 应从客户端项目生成任何使用交互式 WebAssembly 呈现模式的组件,以便它们包含在下载的应用捆绑包中。

将呈现模式应用于组件实例

要将呈现模式应用于组件实例,可利用使用组件的 @rendermodeRazor 指令属性

在以下示例中,交互式服务器端呈现(交互式 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" />

还可以引用直接使用自定义配置实例化的自定义呈现模式实例。 有关详细信息,请参阅本文后面的自定义速记呈现模式部分。

将呈现模式应用于组件定义

要将组件的呈现模式指定为其定义的一部分,请使用 @rendermodeRazor 指令和相应的呈现模式属性。

@page "..."
@rendermode InteractiveServer

将呈现模式应用于特定页面时,通常会将呈现模式应用于组件定义。 默认情况下,可路由页面会使用与呈现页面的 Router 组件相同的呈现模式。

从技术上说,@rendermode 既是 Razor 指令,也是 Razor 指令属性。 语义相似,但存在差异。 @rendermode 指令位于组件定义上,因此引用的呈现模式实例必须是静态的。 @rendermode 指令属性可以采用任何呈现模式实例。

注意

组件作者应避免将组件的实现与特定呈现模式耦合。 相反,组件作者通常应将组件设计为支持任何呈现模式或托管模型。 组件的实现应避免假设其运行位置(服务器或客户端),并在静态呈现时正常降级。 如果未直接实例化组件(例如使用可路由页面组件),或者为所有组件实例指定呈现模式,则可能需要在组件定义中指定呈现模式。

将呈现模式应用于整个应用

若要为整个应用设置呈现模式,请在应用组件层次结构中不是根组件的最高级交互组件上指示呈现模式。

注意

不支持让根组件具有交互性(例如 App 组件)。 因此,App 组件无法直接设置整个应用的呈现模式。

对于基于 Blazor Web 应用项目模板的应用,通常将指定一个分配给整个应用的呈现模式,其中的 Routes 组件在 App 组件中使用 (Components/App.razor):

<Routes @rendermode="InteractiveServer" />

Router 组件会将其呈现模式传播到它路由的页面。

通常还必须在 HeadOutlet 组件上设置相同的交互式呈现模式,该模式也位于从项目模板生成的 Blazor Web 应用的 App 组件中:

<HeadOutlet @rendermode="InteractiveServer" />

如果应用采用交互式客户端(WebAssembly 或自动)呈现模式,并通过 Routes 组件为整个应用启用呈现模式:

  • 将服务器应用的 Components/Layout 文件夹的布局和导航文件放置或移动到 .Client 项目的 Layout 文件夹中。 在 .Client 项目中创建一个 Layout 文件夹(如果不存在)。
  • 将服务器应用的 Components/Pages 文件夹的组件放置或移动到 .Client 项目的 Pages 文件夹中。 在 .Client 项目中创建一个 Pages 文件夹(如果不存在)。
  • 将服务器应用的 Components 文件夹的 Routes 组件放置或移动到 .Client 项目的根文件夹中。

若要在创建 Blazor Web 应用时启用全局交互:

  • Visual Studio:将“交互位置”下拉列表设置为“全局”
  • .NET CLI:使用 -ai|--all-interactive 选项。

有关详细信息,请参阅用于 ASP.NET Core Blazor 的工具

以编程方式应用呈现模式

属性和字段可以分配呈现模式。

本部分所述的第二种方法(按组件实例设置呈现模式)在应用规范调用以下任一方案时特别有用:

  • 应用的一个区域(文件夹)中的组件必须采用静态服务器端呈现(静态 SSR)且仅在服务器上运行。 应用通过基于文件夹路径在 App 组件中的 Routes 组件上设置呈现模式,全局控制呈现模式。
  • 分布在应用中不同位置(不在单个文件夹中)的组件必须采用静态 SSR 且仅在服务器上运行。 应用通过在组件实例中使用 @rendermode 指令设置呈现模式,基于每个组件控制呈现模式。 在 App 组件中使用反射来设置 Routes 组件上的呈现模式。

在这两种情况下,必须采用静态 SSR 的组件还必须强制重新加载整页。

本文后面的精细控制呈现模式部分将举例说明前两种情况。 下面两个小节重点介绍设置呈现模式的基本方法。

按组件定义设置呈现模式

组件定义可以通过专用字段定义呈现模式:

@rendermode renderModeForPage

...

@code {
    private static IComponentRenderMode renderModeForPage = InteractiveServer;
}

按组件实例设置呈现模式

以下示例将交互式服务器端呈现(交互式 SSR)应用于任何请求。

<Routes @rendermode="RenderModeForPage" />

...

@code {
    private IComponentRenderMode? RenderModeForPage => InteractiveServer;
}

本文后面的呈现模式传播部分提供了有关呈现模式传播的其他信息。 精细控制呈现模式部分展示了如何使用前述方法,在应用的特定区域(文件夹)中或为分布在应用各处的特定组件(使用每组件呈现模式分配)采用静态 SSR。

预呈现

预呈现是最初在服务器上呈现页面内容的过程,而无需为呈现的控件启用事件处理程序。 服务器会根据初始请求尽快输出页面的 HTML UI,这会让应用感觉对用户的响应更强。 预呈现还可以通过呈现搜索引擎可用来计算网页排名的初始 HTTP 响应的内容,来改进搜索引擎优化 (SEO)

默认情况下,交互式组件会启用预呈现。

若要禁用组件实例的预呈现,请向呈现模式传递值为 falseprerender 标志:

  • <... @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 应用项目模板的应用,将指定一个分配给整个应用的呈现模式,其中的 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 应用中在本地使用上述组件,请将该组件放置在服务器项目的 Components/Pages 文件夹中。 服务器项目是解决方案的项目,其名称不以 .Client 结尾。 当应用运行时,在浏览器的地址栏中导航到 /render-mode-1

在静态 SSR 期间,Razor 组件页请求由服务器端 ASP.NET Core 中间件管道请求处理进行路由和授权。 路由和授权的专用 Blazor 功能不起作用,因为在服务器端请求处理期间不会呈现 Razor 组件。 在静态 SSR 期间不可用的 Routes 组件中的 Blazor 路由器功能包括显示:

如果应用表现出根级交互性,则在初始静态 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 应用中在本地使用上述组件,请将该组件放置在服务器项目的 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 应用中在本地使用上述组件,请将该组件放置在客户端项目的 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 应用中在本地使用上述组件,请将该组件放置在客户端项目的 Pages 文件夹中。 客户端项目是解决方案的项目,其名称以 .Client 结尾。 当应用运行时,在浏览器的地址栏中导航到 /render-mode-4

呈现模式传播

呈现模式会向下传播组件层次结构。

应用呈现模式的规则:

  • 默认呈现模式为静态。
  • 交互式服务器 (InteractiveServer)、交互式 WebAssembly (InteractiveWebAssembly) 和交互式自动 (InteractiveAuto) 呈现模式可从组件使用,包括对同级组件使用不同的呈现模式。
  • 无法在子组件中切换到其他交互式呈现模式。 例如,服务器组件不能是 WebAssembly 组件的子组件。
  • 从静态父级传递到交互式子组件的参数必须是 JSON 可序列化的。 这意味着无法将呈现片段或子内容从静态父级组件传递到交互式子组件。

以下示例使用了不可路由的非页面 SharedMessage 组件。 与呈现模式无关的 SharedMessage 组件不会使用 @attribute 指令应用呈现模式。 如果要使用 Blazor Web 应用测试这些方案,请将以下组件放置在应用的 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 组件都是预呈现的(默认情况下),并且会在浏览器中显示页面时出现。

  • 建立 SignalR 线路后,第一个使用交互式服务器端呈现(交互式 SSR)的 SharedMessage 组件是交互式的。
  • 下载了 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 应用项目模板采用的方法。

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 组件的区域(文件夹):应用的一个区域(文件夹)中的组件必须采用静态 SSR,并共用相同的路由路径前缀。 应用通过基于文件夹路径在 App 组件中的 Routes 组件上设置呈现模式,全局控制呈现模式。

  • 分布在整个应用中的静态 SSR 组件:分布在应用中不同位置的组件必须采用静态 SSR 且仅在服务器上运行。 纯静态 SSR 组件不在单个文件夹中,也不共享共同的路由路径前缀。 应用通过在组件实例中使用 @rendermode 指令设置呈现模式,基于每个组件控制呈现模式。 在 App 组件中使用反射来设置 Routes 组件上的呈现模式。

在这两种情况下,必须采用静态 SSR 的组件还必须强制重新加载整页。

以下示例使用 HttpContext 级联参数来确定页面是否以静态方式呈现。 nullHttpContext 指示组件以交互方式呈现,可作为应用代码中的信号触发全页面重新加载。

静态 SSR 组件的区域(文件夹)

此小节中所述的方法由 Blazor Web 应用项目模板使用,该模板中包含个人身份验证和全局交互性。

应用的区域(文件夹)包含必须采用静态 SSR 且仅在服务器上运行的组件。 文件夹中的组件共用相同的路由路径前缀。 例如,Blazor Web 应用项目模板的 IdentityRazor 组件位于 Components/Account/Pages 文件夹中,共用根路径前缀 /Account

该文件夹还包含一个 _Imports.razor 文件,该文件将自定义帐户布局应用于文件夹中的组件:

@using BlazorSample.Components.Account.Shared
@layout AccountLayout

Shared 文件夹维护 AccountLayout 布局组件。 该组件利用 HttpContext 来确定组件是否在服务器上呈现。 Identity 组件必须使用静态 SSR 在服务器上呈现,因为它们设置了 Identitycookie。 如果 HttpContext 的值为 null,则组件以交互方式呈现,并通过调用 NavigationManager.Refresh 和将 forceLoad 设置为 true 来执行全页面重新加载。 这会强制使用静态 SSR 重新呈现页面。

Components/Account/Shared/AccountLayout.razor

@inherits LayoutComponentBase
@layout BlazorSample.Components.Layout.MainLayout
@inject NavigationManager NavigationManager

@if (HttpContext is null)
{
    <p>Loading...</p>
}
else
{
    @Body
}

@code {
    [CascadingParameter]
    private HttpContext? HttpContext { get; set; }

    protected override void OnParametersSet()
    {
        if (HttpContext is null)
        {
            NavigationManager.Refresh(forceReload: true);
        }
    }
}

注意

在 Blazor Web 应用项目模板中,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} 占位符更改为适当的值,具体取决于应用程序的其余部分是否应采用全局 InteractiveServerInteractiveWebAssemblyInteractiveAuto 呈现。

Account 文件夹中必须采用静态 SSR 的组件不需要设置布局,因为它通过 _Imports.razor 文件应用,组件因应使用静态 SSR 进行呈现而不设置呈现模式。 对于 Account 文件夹中的组件,无需执行任何其他操作即可强制采用静态 SSR。

分布在整个应用中的静态 SSR 组件

前面的小节中,应用通过在 App 组件中全局设置呈现模式来控制组件的呈现模式。 或者,App 组件还可以采用“每组件”呈现模式来设置呈现模式,从而允许应用中分布的组件强制采用静态 SSR。 本小节介绍该方法。

应用具有一个自定义布局,可应用于应用中分布的组件。 通常,应用的共享组件放置在 Components/Layout 文件夹中。 该组件利用 HttpContext 来确定组件是否在服务器上呈现。 如果 HttpContext 的值为 null,则组件以交互方式呈现,并通过调用 NavigationManager.Refresh 和将 forceLoad 设置为 true 来执行全页面重新加载。 这会触发向服务器发出针对组件的请求。

Components/Layout/StaticSsrLayout.razor

@inherits LayoutComponentBase
@layout MainLayout
@inject NavigationManager NavigationManager

@if (HttpContext is null)
{
    <p>Loading...</p>
}
else
{
    @Body
}

@code {
    [CascadingParameter]
    private HttpContext? HttpContext { get; set; }

    protected override void OnParametersSet()
    {
        if (HttpContext is null)
        {
            NavigationManager.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} 占位符更改为适当的值,具体取决于组件是否应采用 InteractiveServerInteractiveWebAssemblyInteractiveAuto 呈现。

客户端服务在预呈现期间无法解析

假设未为组件或应用禁用预呈现,则 .Client 项目中的组件在服务器上预呈现。 由于服务器无权访问已注册的客户端 Blazor 服务,因此无法将这些服务注入组件,而不会收到“在预呈现期间找不到该服务”的错误。

例如,请考虑 Blazor Web 应用中具有全局交互式 WebAssembly 或交互式自动呈现.Client 项目中的以下 Home 组件。 组件尝试注入 IWebAssemblyHostEnvironment 以获取环境的名称。

@page "/"
@inject IWebAssemblyHostEnvironment Environment

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<p>
    Environment: @Environment.Environment
</p>

未发生编译时错误,但在预呈现期间发生运行时错误:

无法为“BlazorSample.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 APIHttpClient 服务的指导。

  • 建议用于共享框架之外的服务:为服务器上的服务创建自定义服务实现。 在 .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 应用。

Components 文件夹中的顶层导入文件 (Components/_Imports.razor) 将其引用注入到文件夹层次结构中的所有组件,包括 App 组件 (App.razor)。 App 组件始终静态呈现,即使禁用了页面组件的预呈现也是如此。 因此,通过顶层导入文件注入服务会导致在页面组件中解析该服务的两个实例。

若要解决此问题,请在位于 Pages 文件夹中的新导入文件 (Components/Pages/_Imports.razor) 中注入服务。 从该位置,服务在页面组件中仅会被解析一次。

其他资源