注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
本文解释了 Razor 组件在 Blazor Web Apps 和 Blazor Server 应用中服务器呈现组件的预渲染方案。
预呈现 是从服务器静态呈现页面内容以尽快将 HTML 传送到浏览器的过程。 将预呈现的内容快速显示给用户后,将呈现具有活动事件处理程序的交互式内容,并替换之前呈现的任何内容。 预呈现还可以通过呈现搜索引擎用于计算网页排名的初始 HTTP 响应的内容,来改进搜索引擎优化 (SEO)。
默认情况下,交互式组件会启用预呈现。
具有交互式路由的内部导航不使用预呈现,因为页面已经是交互式的。 有关详细信息,请参阅 静态路由与交互式路由 和 交互式路由和预呈现。
组件生命周期事件OnAfterRender{Async}不会在预呈现时调用,仅在组件以交互方式呈现后调用。
禁用预呈现
预呈现可能会使应用复杂化,因为应用的 Razor 组件必须呈现两次:一次用于预呈现,一次用于设置交互性。 如果组件设置为在 WebAssembly 上运行,则还必须设计组件,以便它们可从服务器和客户端运行。
若要禁用组件实例的预呈现,请向呈现模式传递值为 prerender 的 false 标志:
<... @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)" />
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
不支持使根组件(如 App 组件)与根组件定义文件 (@rendermode) 顶部的 .razor 指令交互。 因此,App 组件无法直接禁用预呈现。
使用上述技术禁用预呈现只会对顶级呈现模式生效。 如果父组件指定了渲染模式,则会忽略其子级的预渲染设置。
保留预渲染状态
在不保留预呈现状态的情况下,在预呈现期间使用的状态将丢失,并且在完全加载应用时必须重新创建。 如果任何状态是异步创建的,那么当组件重新渲染时,用户界面可能会闪烁,因为预渲染的用户界面将被替换。 有关如何在预呈现期间保留状态的指导,请参阅 ASP.NET 核心 Blazor 预呈现状态持久性。
客户端服务在预呈现期间无法解析
假设未为组件或应用禁用预呈现,则 .Client 项目中的组件在服务器上预呈现。 由于服务器无权访问已注册的客户端 Blazor 服务,因此无法将这些服务注入组件,而不会收到“在预呈现期间找不到该服务”的错误。
例如,请考虑具有Home的 .Client 内 Blazor Web App 项目中的以下 组件。 组件尝试注入 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 不是服务器上的已注册服务。
请考虑以下任一方法来解决此方案:
除了客户端之外,在服务器上注册服务
如果服务支持服务器执行,则除了客户端之外,在服务器上注册该服务,以便在预呈现期间可用。 有关此方案的示例,请参阅《调用 Web API》文章中HttpClient外部 Web API 部分的Blazor Web App。
注入应用在预呈现期间可以使用的服务
在某些情况下,应用可以在预呈现期间在服务器上使用服务,并在客户端上使用其他服务。
例如,以下代码通过从 Microsoft.Extensions.Hosting.Abstractions注入来获取应用的环境,无论代码是在服务器上还是在客户端上运行:
private string? environmentName;
public Home(IHostEnvironment? serverEnvironment = null,
IWebAssemblyHostEnvironment? wasmEnvironment = null)
{
environmentName = serverEnvironment?.EnvironmentName;
environmentName ??= wasmEnvironment?.Environment;
}
但是,此方法向不需要的客户端项目添加额外的依赖项。
使服务成为可选
如果使用以下任一方法在预呈现期间不需要该服务,请将其设置为可选。
以下示例使用构造函数注入:IWebAssemblyHostEnvironment
private string? environmentName;
public Home(IWebAssemblyHostEnvironment? env = null)
{
environmentName = env?.Environment;
}
或者,如果服务可用,请注入 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;
}
}
}
创建服务抽象
如果在服务器上需要其他服务实现,请在服务器和客户端项目中创建服务抽象并创建服务的实现。 在每个项目中注册服务。 根据需要将自定义服务抽象注入组件。 然后,该组件只依赖于自定义服务抽象。
在这种情况下 IWebAssemblyHostEnvironment,我们可以重复使用现有接口,而不是创建新的接口:
ServerHostEnvironment.cs:
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Components;
public class ServerHostEnvironment(IWebHostEnvironment env, NavigationManager nav) :
IWebAssemblyHostEnvironment
{
public string Environment => env.EnvironmentName;
public string BaseAddress => nav.BaseUri;
}
在服务器项目的 Program 文件中,注册服务:
builder.Services.TryAddScoped<IWebAssemblyHostEnvironment, ServerHostEnvironment>();
此时, IWebAssemblyHostEnvironment 服务可以 注入到交互式 WebAssembly 或自动化组件中,该组件也在服务器上预渲染。
禁用组件的预呈现
为组件或整个应用禁用预呈现。 有关详细信息,请参阅 “禁用预呈现 ”部分。