将 ASP.NET 核心Razor组件与托管Blazor WebAssembly解决方案中的 MVC 或 Razor Pages 集成

注意

托管 Blazor WebAssembly 解决方案仍受支持,但项目模板已删除,在 .NET 8 或更高版本中不再受支持。 本文显示在最多 .NET 7 的目录中供参考,但请注意,.NET 7 是 不再受支持的标准支持术语 版本。

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略

本文介绍 Razor 托管 Blazor WebAssembly 应用的组件集成方案,包括预呈现 Razor 服务器上的组件。

重要

各 ASP.NET Core 版本中的框架更改导致本文提供了多组不同的说明。 在使用本文的指导之前,请确认本文顶部的文档版本选择器与要用于应用的 ASP.NET Core 版本匹配。

预呈现通过呈现搜索引擎可用来计算网页排名的初始 HTTP 响应的内容,可以改进搜索引擎优化 (SEO)

解决方案配置

预呈现配置

若要设置托管 Blazor WebAssembly 应用的预呈现,请执行以下操作:

  1. 将 Blazor WebAssembly 应用托管在 ASP.NET Core 应用中。 可以将独立的 Blazor WebAssembly 应用添加到 ASP.NET Core 解决方案中,也可以使用通过托管选项从 Blazor WebAssembly 项目模板创建的托管 Blazor WebAssembly 应用:

    • Visual Studio:在“其他信息”对话框中,创建 Blazor WebAssembly 应用时选中“托管的 ASP.NET Core”复选框。 在本文的示例中,该解决方案的名称为 BlazorHosted
    • Visual Studio Code/.NET CLI 命令行界面:dotnet new blazorwasm -ho(使用 -ho|--hosted 选项)。 使用 -o|--output {LOCATION} 选项为解决方案创建文件夹,并设置解决方案的项目命名空间。 在本文的示例中,该解决方案的名称为 BlazorHosted (dotnet new blazorwasm -ho -o BlazorHosted)。

    对于本文中的示例,托管解决方案的名称(程序集名称)为 BlazorHosted。 客户端项目的命名空间为 BlazorHosted.Client,服务器项目的命名空间为 BlazorHosted.Server

  2. 从 Blazor WebAssemblyClient 项目中删除 wwwroot/index.html 文件。

  3. Client 项目中,删除 Program.cs 中的以下行:

    - builder.RootComponents.Add<App>("#app");
    - builder.RootComponents.Add<HeadOutlet>("head::after");
    
  4. _Host.cshtml 文件添加到 Server 项目的 Pages 文件夹。 可以在命令行界面中使用 Visual Studio 或 .NET CLI 通过 dotnet new blazorserver -o BlazorServer 命令从 Blazor Server 模板创建的项目获取文件(-o BlazorServer 选项为项目创建一个文件夹)。 将文件放入 Server 项目的 Pages 文件夹后,对文件进行以下更改。

    _Host.cshtml 文件进行以下更改:

    • 更新文件顶部的 Pages 命名空间,使其与 Server 应用页的命名空间匹配。 以下示例中的 {APP NAMESPACE} 占位符表示提供 _Host.cshtml 文件的赞助商应用页的命名空间:

      删除:

      - @namespace {APP NAMESPACE}.Pages
      

      添加:

      @namespace BlazorHosted.Server.Pages
      
    • 在文件顶部,为 Client 项目添加 @using 指令:

      @using BlazorHosted.Client
      
    • 更新样式表链接,以指向 WebAssembly 项目的样式表。 在下面的示例中,客户端项目的命名空间为 BlazorHosted.Client{APP NAMESPACE} 占位符表示提供 _Host.cshtml 文件的赞助商应用的命名空间。 更新 HeadOutlet 组件的组件标记帮助程序(<component> 标记),以预呈现该组件。

      删除:

      - <link href="css/site.css" rel="stylesheet" />
      - <link href="{APP NAMESPACE}.styles.css" rel="stylesheet" />
      - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
      

      添加:

      <link href="css/app.css" rel="stylesheet" />
      <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />
      <component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />
      

      注意

      就地保留请求启动样式 (css/bootstrap/bootstrap.min.css) 表的 <link> 元素。

    • 更新 Blazor 脚本源,以使用客户端 Blazor WebAssembly 脚本:

      删除:

      - <script src="_framework/blazor.server.js"></script>
      

      添加:

      <script src="_framework/blazor.webassembly.js"></script>
      
    • 更新组件标记帮助程序render-mode,以使用 WebAssemblyPrerendered 预呈现根 App 组件:

      删除:

      - <component type="typeof(App)" render-mode="ServerPrerendered" />
      

      添加:

      <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
      

      重要

      身份验证终结点(/authentication/ 路径段)不支持预呈现。 有关详细信息,请参阅 ASP.NET Core Blazor WebAssembly 其他安全方案

  5. Server 项目的 Program.cs 文件中,将回退终结点从 index.html 文件更改为 _Host.cshtml 页面:

    删除:

    - app.MapFallbackToFile("index.html");
    

    添加:

    app.MapFallbackToPage("/_Host");
    
  6. 如果 ClientServer 项目在预呈现期间使用一个或多个通用服务,则将服务注册分解为可以从这两个项目调用的方法。 有关详细信息,请参阅 ASP.NET Core Blazor 依赖项注入

  7. 运行 Server 项目。 托管 Blazor WebAssembly 应用由客户端的 Server 项目预呈现。

用于将 Razor 组件嵌入页面或视图的配置

以下用于将组件从ClientBlazor WebAssembly应用嵌入Razor到服务器应用的页面或视图中的部分和示例需要其他配置。

Server 项目必须具有以下文件和文件夹。

Razor Pages:

  • Pages/Shared/_Layout.cshtml
  • Pages/Shared/_Layout.cshtml.css
  • Pages/_ViewImports.cshtml
  • Pages/_ViewStart.cshtml

MVC:

  • Views/Shared/_Layout.cshtml
  • Views/Shared/_Layout.cshtml.css
  • Views/_ViewImports.cshtml
  • Views/_ViewStart.cshtml

可以通过使用以下内容从 ASP.NET Core 项目模板生成应用来获取上述文件:

  • Visual Studio 的新项目创建工具。
  • 打开命令行界面,执行 dotnet new webapp -o {PROJECT NAME} (Razor Pages) 或 dotnet new mvc -o {PROJECT NAME} (MVC)。 占位符值为 {PROJECT NAME} 的选项 -o|--output 提供应用名称,并为该应用创建一个文件夹。

更新导入的 _ViewImports.cshtml 文件中的命名空间,使其与接收文件的 Server 项目所使用的命名空间相匹配。

Pages/_ViewImports.cshtml (Razor Pages):

@using BlazorHosted.Server
@namespace BlazorHosted.Server.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Views/_ViewImports.cshtml (MVC):

@using BlazorHosted.Server
@using BlazorHosted.Server.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

更新导入的布局文件(对于 Razor Pages,为 Pages/Shared/_Layout.cshtml,对于 MVC 则为 Views/Shared/_Layout.cshtml)。

首先,从赞助商项目中删除标题和样式表,以下示例中为 RPDonor.styles.css{PROJECT NAME} 占位符表示赞助商项目的应用名称。

- <title>@ViewData["Title"] - {PROJECT NAME}</title>
- <link rel="stylesheet" href="~/RPDonor.styles.css" asp-append-version="true" />

在布局文件中包括 Client 项目的样式。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client。 可以同时更新的 <title> 元素。

将以下行置于布局文件的 <head> 内容中:

<title>@ViewData["Title"] - BlazorHosted</title>
<link href="css/app.css" rel="stylesheet" />
<link rel="stylesheet" href="BlazorHosted.Client.styles.css" asp-append-version="true" />
<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />

导入的布局包含两个导航链接 HomeIndex 页)和 Privacy。 若要使 Home 链接指向托管的 Blazor WebAssembly 应用,请更改超链接:

- <a class="navbar-brand" asp-area="" asp-page="/Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>

在 MVC 布局文件中:

- <a class="navbar-brand" asp-area="" asp-controller="Home" 
-     asp-action="Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-controller="Home" 
-     asp-action="Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>

更新 <footer> 元素的应用名称。 以下示例使用应用名称 BlazorHosted

- &copy; {DATE} - {DONOR NAME} - <a asp-area="" asp-page="/Privacy">Privacy</a>
+ &copy; {DATE} - BlazorHosted - <a asp-area="" asp-page="/Privacy">Privacy</a>

在前面的示例中,{DATE} 占位符表示从 Razor Pages 或 MVC 项目模板生成的应用中的版权日期。

若要使 Privacy 链接指向 privacy 页 (Razor Pages),请将 privacy 页添加到 Server 项目。

Server 项目中的 Pages/Privacy.cshtml

@page
@model PrivacyModel
@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

对于基于 MVC 的 privacy 视图,请在 Server 项目中创建一个 privacy 视图。

Server 项目中的 View/Home/Privacy.cshtml

@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

在 MVC 应用的 Home 控制器中,返回视图。

将下列代码添加到 Controllers/HomeController.cs

public IActionResult Privacy()
{
    return View();
}

如果从赞助商应用导入文件,请确保更新文件中的任何命名空间,以匹配 Server 项目的命名空间(例如 BlazorHosted.Server)。

从赞助商项目的 wwwroot 文件夹中将静态资产导入到 Server 项目:

  • wwwroot/css 文件夹和内容
  • wwwroot/js 文件夹和内容
  • wwwroot/lib 文件夹和内容

如果从 ASP.NET Core 项目模板创建了赞助商项目,但未修改文件,则可以将整个 wwwroot 文件夹从赞助商项目复制到 Server 项目中,并删除 favicon 图标文件。

警告

避免将静态资产放入 ClientServer wwwroot 文件夹中。 如果这两个文件夹中存在相同文件,则会引发异常,因为静态资产共享同一个 Web 根路径。 因此,在任意一个 wwwroot 文件夹(而不是两者)中托管静态资产。

采用前面的配置后,将 Razor 组件嵌入到 Server 项目的页面或视图中。 使用本文以下部分的指南:

  • 使用组件标记帮助程序,通过页面或视图呈现组件
  • 使用 CSS 选择器,通过页面或视图呈现组件

使用组件标记帮助程序,通过页面或视图呈现组件

配置解决方案(包括其他配置)后,组件标记帮助程序支持两种呈现模式来通过页面或视图呈现 Blazor WebAssembly 应用中的组件:

在下面的 Razor Pages 示例中,页面中呈现了 Counter 组件。 页面的呈现部分包含了 Blazor WebAssembly 脚本,以使组件成为交互式组件。 为客户端项目的 Pages 命名空间添加 @using 指令,以避免将 Counter 组件的整个命名空间用于组件标记帮助程序 ({ASSEMBLY NAME}.Pages.Counter)。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client

Server 项目中,Pages/RazorPagesCounter1.cshtml

@page
@using BlazorHosted.Client.Pages

<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" />

@section Scripts {
    <script src="_framework/blazor.webassembly.js"></script>
}

运行 Server 项目。 导航到 /razorpagescounter1 中的 Razor 页。 预呈现的 Counter 组件嵌入在页面中。

RenderMode 配置组件是否:

  • 在页面中预呈现。
  • 在页面上呈现为静态 HTML,或者包含从用户代理启动 Blazor 应用所需的信息。

有关组件标记帮助程序的详细信息(包括传递参数和 RenderMode 配置),请参阅 ASP.NET Core 中的组件标记帮助程序

可能需要额外的工作,具体取决于组件使用的静态资源以及布局页面在应用中的组织方式。 通常,脚本添加到了页面或视图的 Scripts 呈现部分,样式表添加到了布局的 <head> 元素内容。

通过呈现片段设置子内容

组件标记帮助程序不支持接收子内容的 RenderFragment 委托(例如 param-ChildContent="...")。 我们建议创建一个 Razor 组件 (.razor),该组件引用要呈现的组件以及要传递的子内容,然后从页面或视图中调用 Razor 组件。

确保在发布时不会剪裁顶级预呈现组件

如果组件标记帮助程序直接引用库中的一个组件,而该组件在发布时需要剪裁,则组件可能会在发布期间被剪裁掉,因为客户端应用代码中没有对该组件的引用。 因此,不会预呈现组件,而是在输出中留有空白位置。 如果发生这种情况,请通过向客户端应用中的任何类添加 DynamicDependency 属性来指示剪裁器保留库组件。 若要保留名为 SomeLibraryComponentToBePreserved 的组件,请向任何组件添加以下内容:

@using System.Diagnostics.CodeAnalysis
@attribute [DynamicDependency(DynamicallyAccessedMemberTypes.All, 
    typeof(SomeLibraryComponentToBePreserved))]

上述方法通常不是必需的,因为应用通常会预呈现其组件(未剪裁),这反过来又引用库中的组件(导致它们也不会被剪裁)。 只有在库需要剪裁时,才将 DynamicDependency 显式用于直接预呈现库组件。

使用 CSS 选择器,通过页面或视图呈现组件

配置解决方案(包括其他配置)后,将根组件添加到 Program.cs 文件中的托管 Blazor WebAssembly 解决方案的 Client 项目。 下面的示例使用 CSS 选择器将 Counter 组件声明为根组件,该选择器会选择 idcounter-component 匹配的元素。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client

Client 项目的 Program.cs 文件中,将项目的 Razor 组件的命名空间添加到文件顶部:

using BlazorHosted.Client.Pages;

Program.cs 中建立 builder 后,将 Counter 组件添加为根组件:

builder.RootComponents.Add<Counter>("#counter-component");

在下面的 Razor Pages 示例中,页面中呈现了 Counter 组件。 页面的呈现部分包含了 Blazor WebAssembly 脚本,以使组件成为交互式组件。

Server 项目中,Pages/RazorPagesCounter2.cshtml

@page

<div id="counter-component">Loading...</div>

@section Scripts {
    <script src="_framework/blazor.webassembly.js"></script>
}

运行 Server 项目。 导航到 /razorpagescounter2 中的 Razor 页。 预呈现的 Counter 组件嵌入在页面中。

可能需要额外的工作,具体取决于组件使用的静态资源以及布局页面在应用中的组织方式。 通常,脚本添加到了页面或视图的 Scripts 呈现部分,样式表添加到了布局的 <head> 元素内容。

注意

如果 Blazor WebAssembly 应用已预呈现,并且使用 CSS 选择器同时集成到 Razor Pages 或 MVC 应用中,则前面的示例将引发 JSException。 如果导航到 Client 项目的 Razor 组件之一,或者导航到具有嵌入组件的 Server 的页面或视图,会引发一个或多个 JSException

这是正常行为,因为预呈现和集成具有可路由 Razor 组件的 Blazor WebAssembly 应用与使用 CSS 选择器不兼容。

如果你一直在使用前面部分中的示例,并且只想看到 CSS 选择器在示例应用中工作,请注释掉 Client 项目 Program.cs 文件的 App 根组件的规范:

- builder.RootComponents.Add<App>("#app");
+ //builder.RootComponents.Add<App>("#app");

通过使用 CSS 选择器的嵌入式 Razor 组件(例如前面示例的 /razorpagescounter2)导航到页面或视图。 页面或视图随嵌入式组件一起加载,而嵌入式组件按预期运行。

保留预呈现状态

在不保留预呈现状态的情况下,在预呈现期间使用的状态将丢失,并且在完全加载应用时必须重新创建。 如果任何状态都是异步设置的,则 UI 可能会闪烁,因为预呈现 UI 将替换为临时占位符,然后再次完全呈现。

要保留预呈现组件的状态,请使用“保留组件状态”标记帮助程序参考源)。 在预呈现组件的应用的 _Host 页面的结束 </body> 标记内添加标记帮助程序的标记 <persist-component-state />

注意

指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)

Pages/_Host.cshtml托管Blazor WebAssembly应用中,WebAssembly 预呈现 (WebAssemblyPrerendered) 的应用:Blazor

<body>
    ...

    <persist-component-state />
</body>

决定要使用 PersistentComponentState 服务保留的状态。 PersistentComponentState.RegisterOnPersisting 注册回调以在暂停应用之前保留组件状态。 在应用程序恢复时检索状态。

如下示例中:

  • {TYPE} 占位符表示要持久保存的数据类型(例如 WeatherForecast[])。
  • {TOKEN} 占位符是状态标识符字符串(例如 fetchdata)。
@implements IDisposable
@inject PersistentComponentState ApplicationState

...

@code {
    private {TYPE} data;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistData);

        if (!ApplicationState.TryTakeFromJson<{TYPE}>(
            "{TOKEN}", out var restored))
        {
            data = await ...;
        }
        else
        {
            data = restored!;
        }
    }

    private Task PersistData()
    {
        ApplicationState.PersistAsJson("{TOKEN}", data);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

以下示例是基于 Blazor 项目模板的托管的 Blazor WebAssembly 应用中 FetchData 组件的更新版本。 WeatherForecastPreserveState 组件在预呈现期间保留天气预报状态,然后检索状态以初始化组件。 保留组件状态标记帮助程序在所有组件调用之后保留组件状态。

Pages/WeatherForecastPreserveState.razor:

@page "/weather-forecast-preserve-state"
@using BlazorSample.Shared
@implements IDisposable
@inject IWeatherForecastService WeatherForecastService
@inject PersistentComponentState ApplicationState

<PageTitle>Weather Forecast</PageTitle>

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[] forecasts = Array.Empty<WeatherForecast>();
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistForecasts);

        if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
            "fetchdata", out var restored))
        {
            forecasts = 
                await WeatherForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
        }
        else
        {
            forecasts = restored!;
        }
    }

    private Task PersistForecasts()
    {
        ApplicationState.PersistAsJson("fetchdata", forecasts);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

通过使用在预呈现期间使用的相同状态来初始化组件,将只执行一次成本高昂的初始化步骤。 呈现的 UI 也与预呈现 UI 相匹配,因此浏览器不会闪烁。

持久预呈现状态将传输到客户端,用于还原组件状态。 对于托管 Blazor WebAssembly 应用中的预呈现,数据将公开给浏览器,并且不得包含敏感的私有信息。

其他 Blazor WebAssembly 资源

预呈现通过呈现搜索引擎可用来计算网页排名的初始 HTTP 响应的内容,可以改进搜索引擎优化 (SEO)

解决方案配置

预呈现配置

若要设置托管 Blazor WebAssembly 应用的预呈现,请执行以下操作:

  1. 将 Blazor WebAssembly 应用托管在 ASP.NET Core 应用中。 可以将独立的 Blazor WebAssembly 应用添加到 ASP.NET Core 解决方案中,也可以使用通过托管选项从 Blazor WebAssembly 项目模板创建的托管 Blazor WebAssembly 应用:

    • Visual Studio:在“其他信息”对话框中,创建 Blazor WebAssembly 应用时选中“托管的 ASP.NET Core”复选框。 在本文的示例中,该解决方案的名称为 BlazorHosted
    • Visual Studio Code/.NET CLI 命令行界面:dotnet new blazorwasm -ho(使用 -ho|--hosted 选项)。 使用 -o|--output {LOCATION} 选项为解决方案创建文件夹,并设置解决方案的项目命名空间。 在本文的示例中,该解决方案的名称为 BlazorHosted (dotnet new blazorwasm -ho -o BlazorHosted)。

    对于本文中的示例,客户端项目的命名空间为 BlazorHosted.Client,服务器项目的命名空间为 BlazorHosted.Server

  2. 从 Blazor WebAssemblyClient 项目中删除 wwwroot/index.html 文件。

  3. Client 项目中,删除 Program.cs 中的以下行:

    - builder.RootComponents.Add<App>("#app");
    - builder.RootComponents.Add<HeadOutlet>("head::after");
    
  4. _Host.cshtml_Layout.cshtml 文件添加到 Server 项目的 Pages 文件夹。 可以在命令行界面中使用 Visual Studio 或 .NET CLI 通过 dotnet new blazorserver -o BlazorServer 命令从 Blazor Server 模板创建的项目获取文件(-o BlazorServer 选项为项目创建一个文件夹)。 将文件放入 Server 项目的 Pages 文件夹后,对文件进行以下更改。

    重要

    需要将布局页 (_Layout.cshtml) 和 HeadOutlet 组件的组件标记帮助程序一起使用来控制 <head> 内容,例如页面标题(PageTitle 组件)和其他头元素(HeadContent 组件)。 有关详细信息,请参阅在 ASP.NET Core Blazor 应用中控制头内容

    _Layout.cshtml 文件进行以下更改:

    • 更新文件顶部的 Pages 命名空间,使其与 Server 应用页的命名空间匹配。 以下示例中的 {APP NAMESPACE} 占位符表示提供 _Layout.cshtml 文件的赞助商应用页的命名空间:

      删除:

      - @namespace {APP NAMESPACE}.Pages
      

      添加:

      @namespace BlazorHosted.Server.Pages
      
    • 在文件顶部,为 Client 项目添加 @using 指令:

      @using BlazorHosted.Client
      
    • 更新样式表链接,以指向 WebAssembly 项目的样式表。 在下面的示例中,客户端项目的命名空间为 BlazorHosted.Client{APP NAMESPACE} 占位符表示提供 _Layout.cshtml 文件的赞助商应用的命名空间。 更新 HeadOutlet 组件的组件标记帮助程序(<component> 标记),以预呈现该组件。

      删除:

      - <link href="css/site.css" rel="stylesheet" />
      - <link href="{APP NAMESPACE}.styles.css" rel="stylesheet" />
      - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
      

      添加:

      <link href="css/app.css" rel="stylesheet" />
      <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />
      <component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />
      

      注意

      就地保留请求启动样式 (css/bootstrap/bootstrap.min.css) 表的 <link> 元素。

    • 更新 Blazor 脚本源,以使用客户端 Blazor WebAssembly 脚本:

      删除:

      - <script src="_framework/blazor.server.js"></script>
      

      添加:

      <script src="_framework/blazor.webassembly.js"></script>
      

    _Host.cshtml 文件中:

    • Pages 命名空间更改为 Client 项目的命名空间。 {APP NAMESPACE} 占位符表示提供 _Host.cshtml 文件的赞助商应用页的命名空间:

      删除:

      - @namespace {APP NAMESPACE}.Pages
      

      添加:

      @namespace BlazorHosted.Client
      
    • 更新组件标记帮助程序render-mode,以使用 WebAssemblyPrerendered 预呈现根 App 组件:

      删除:

      - <component type="typeof(App)" render-mode="ServerPrerendered" />
      

      添加:

      <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
      

      重要

      身份验证终结点(/authentication/ 路径段)不支持预呈现。 有关详细信息,请参阅 ASP.NET Core Blazor WebAssembly 其他安全方案

  5. ServerProgram.cs 项目的终结点映射中,将回退从 index.html 文件更改为 _Host.cshtml 页面:

    删除:

    - app.MapFallbackToFile("index.html");
    

    添加:

    app.MapFallbackToPage("/_Host");
    
  6. 如果 ClientServer 项目在预呈现期间使用一个或多个通用服务,则将服务注册分解为可以从这两个项目调用的方法。 有关详细信息,请参阅 ASP.NET Core Blazor 依赖项注入

  7. 运行 Server 项目。 托管 Blazor WebAssembly 应用由客户端的 Server 项目预呈现。

用于将 Razor 组件嵌入页面或视图的配置

以下用于将组件从ClientBlazor WebAssembly应用嵌入Razor到服务器应用的页面或视图中的部分和示例需要其他配置。

Server 项目必须具有以下文件和文件夹。

Razor Pages:

  • Pages/Shared/_Layout.cshtml
  • Pages/Shared/_Layout.cshtml.css
  • Pages/_ViewImports.cshtml
  • Pages/_ViewStart.cshtml

MVC:

  • Views/Shared/_Layout.cshtml
  • Views/Shared/_Layout.cshtml.css
  • Views/_ViewImports.cshtml
  • Views/_ViewStart.cshtml

重要

需要将布局页 (_Layout.cshtml) 和 HeadOutlet 组件的组件标记帮助程序一起使用来控制 <head> 内容,例如页面标题(PageTitle 组件)和其他头元素(HeadContent 组件)。 有关详细信息,请参阅在 ASP.NET Core Blazor 应用中控制头内容

可以通过使用以下内容从 ASP.NET Core 项目模板生成应用来获取上述文件:

  • Visual Studio 的新项目创建工具。
  • 打开命令行界面,执行 dotnet new webapp -o {PROJECT NAME} (Razor Pages) 或 dotnet new mvc -o {PROJECT NAME} (MVC)。 占位符值为 {PROJECT NAME} 的选项 -o|--output 提供应用名称,并为该应用创建一个文件夹。

更新导入的 _ViewImports.cshtml 文件中的命名空间,使其与接收文件的 Server 项目所使用的命名空间相匹配。

Pages/_ViewImports.cshtml (Razor Pages):

@using BlazorHosted.Server
@namespace BlazorHosted.Server.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Views/_ViewImports.cshtml (MVC):

@using BlazorHosted.Server
@using BlazorHosted.Server.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

更新导入的布局文件(对于 Razor Pages,为 Pages/Shared/_Layout.cshtml,对于 MVC 则为 Views/Shared/_Layout.cshtml)。

首先,从赞助商项目中删除标题和样式表,以下示例中为 RPDonor.styles.css{PROJECT NAME} 占位符表示赞助商项目的应用名称。

- <title>@ViewData["Title"] - {PROJECT NAME}</title>
- <link rel="stylesheet" href="~/RPDonor.styles.css" asp-append-version="true" />

在布局文件中包括 Client 项目的样式。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client。 可以同时更新的 <title> 元素。

将以下行置于布局文件的 <head> 内容中:

<title>@ViewData["Title"] - BlazorHosted</title>
<link href="css/app.css" rel="stylesheet" />
<link rel="stylesheet" href="BlazorHosted.Client.styles.css" asp-append-version="true" />
<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />

导入的布局包含两个导航链接 HomeIndex 页)和 Privacy。 若要使 Home 链接指向托管的 Blazor WebAssembly 应用,请更改超链接:

- <a class="navbar-brand" asp-area="" asp-page="/Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>

在 MVC 布局文件中:

- <a class="navbar-brand" asp-area="" asp-controller="Home" 
-     asp-action="Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-controller="Home" 
-     asp-action="Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>

更新 <footer> 元素的应用名称。 以下示例使用应用名称 BlazorHosted

- &copy; {DATE} - {DONOR NAME} - <a asp-area="" asp-page="/Privacy">Privacy</a>
+ &copy; {DATE} - BlazorHosted - <a asp-area="" asp-page="/Privacy">Privacy</a>

在前面的示例中,{DATE} 占位符表示从 Razor Pages 或 MVC 项目模板生成的应用中的版权日期。

若要使 Privacy 链接指向 privacy 页 (Razor Pages),请将 privacy 页添加到 Server 项目。

Server 项目中的 Pages/Privacy.cshtml

@page
@model PrivacyModel
@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

对于基于 MVC 的 privacy 视图,请在 Server 项目中创建一个 privacy 视图。

Server 项目中的 View/Home/Privacy.cshtml

@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

在 MVC 应用的 Home 控制器中,返回视图。

将下列代码添加到 Controllers/HomeController.cs

public IActionResult Privacy()
{
    return View();
}

如果从赞助商应用导入文件,请确保更新文件中的任何命名空间,以匹配 Server 项目的命名空间(例如 BlazorHosted.Server)。

从赞助商项目的 wwwroot 文件夹中将静态资产导入到 Server 项目:

  • wwwroot/css 文件夹和内容
  • wwwroot/js 文件夹和内容
  • wwwroot/lib 文件夹和内容

如果从 ASP.NET Core 项目模板创建了赞助商项目,但未修改文件,则可以将整个 wwwroot 文件夹从赞助商项目复制到 Server 项目中,并删除 favicon 图标文件。

警告

避免将静态资产放入 ClientServer wwwroot 文件夹中。 如果这两个文件夹中存在相同文件,则会引发异常,因为每个文件夹中的静态资产共享同一个 Web 根路径。 因此,在任意一个 wwwroot 文件夹(而不是两者)中托管静态资产。

采用前面的配置后,将 Razor 组件嵌入到 Server 项目的页面或视图中。 使用本文以下部分的指南:

  • 使用组件标记帮助程序,通过页面或视图呈现组件
  • 使用 CSS 选择器,通过页面或视图呈现组件

使用组件标记帮助程序,通过页面或视图呈现组件

配置解决方案(包括其他配置)后,组件标记帮助程序支持两种呈现模式来通过页面或视图呈现 Blazor WebAssembly 应用中的组件:

在下面的 Razor Pages 示例中,页面中呈现了 Counter 组件。 页面的呈现部分包含了 Blazor WebAssembly 脚本,以使组件成为交互式组件。 为客户端项目的 Pages 命名空间添加 @using 指令,以避免将 Counter 组件的整个命名空间用于组件标记帮助程序 ({ASSEMBLY NAME}.Pages.Counter)。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client

Server 项目中,Pages/RazorPagesCounter1.cshtml

@page
@using BlazorHosted.Client.Pages

<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" />

@section Scripts {
    <script src="_framework/blazor.webassembly.js"></script>
}

运行 Server 项目。 导航到 /razorpagescounter1 中的 Razor 页。 预呈现的 Counter 组件嵌入在页面中。

RenderMode 配置组件是否:

  • 在页面中预呈现。
  • 在页面上呈现为静态 HTML,或者包含从用户代理启动 Blazor 应用所需的信息。

有关组件标记帮助程序的详细信息(包括传递参数和 RenderMode 配置),请参阅 ASP.NET Core 中的组件标记帮助程序

可能需要额外的工作,具体取决于组件使用的静态资源以及布局页面在应用中的组织方式。 通常,脚本添加到了页面或视图的 Scripts 呈现部分,样式表添加到了布局的 <head> 元素内容。

通过呈现片段设置子内容

组件标记帮助程序不支持接收子内容的 RenderFragment 委托(例如 param-ChildContent="...")。 我们建议创建一个 Razor 组件 (.razor),该组件引用要呈现的组件以及要传递的子内容,然后从页面或视图中调用 Razor 组件。

确保在发布时不会剪裁顶级预呈现组件

如果组件标记帮助程序直接引用库中的一个组件,而该组件在发布时需要剪裁,则组件可能会在发布期间被剪裁掉,因为客户端应用代码中没有对该组件的引用。 因此,不会预呈现组件,而是在输出中留有空白位置。 如果发生这种情况,请通过向客户端应用中的任何类添加 DynamicDependency 属性来指示剪裁器保留库组件。 若要保留名为 SomeLibraryComponentToBePreserved 的组件,请向任何组件添加以下内容:

@using System.Diagnostics.CodeAnalysis
@attribute [DynamicDependency(DynamicallyAccessedMemberTypes.All, 
    typeof(SomeLibraryComponentToBePreserved))]

上述方法通常不是必需的,因为应用通常会预呈现其组件(未剪裁),这反过来又引用库中的组件(导致它们也不会被剪裁)。 只有在库需要剪裁时,才将 DynamicDependency 显式用于直接预呈现库组件。

使用 CSS 选择器,通过页面或视图呈现组件

配置解决方案(包括其他配置)后,将根组件添加到 Program.cs 文件中的托管 Blazor WebAssembly 解决方案的 Client 项目。 下面的示例使用 CSS 选择器将 Counter 组件声明为根组件,该选择器会选择 idcounter-component 匹配的元素。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client

Client 项目的 Program.cs 文件中,将项目的 Razor 组件的命名空间添加到文件顶部:

using BlazorHosted.Client.Pages;

Program.cs 中建立 builder 后,将 Counter 组件添加为根组件:

builder.RootComponents.Add<Counter>("#counter-component");

在下面的 Razor Pages 示例中,页面中呈现了 Counter 组件。 页面的呈现部分包含了 Blazor WebAssembly 脚本,以使组件成为交互式组件。

Server 项目中,Pages/RazorPagesCounter2.cshtml

@page

<div id="counter-component">Loading...</div>

@section Scripts {
    <script src="_framework/blazor.webassembly.js"></script>
}

运行 Server 项目。 导航到 /razorpagescounter2 中的 Razor 页。 预呈现的 Counter 组件嵌入在页面中。

可能需要额外的工作,具体取决于组件使用的静态资源以及布局页面在应用中的组织方式。 通常,脚本添加到了页面或视图的 Scripts 呈现部分,样式表添加到了布局的 <head> 元素内容。

注意

如果 Blazor WebAssembly 应用已预呈现,并且使用 CSS 选择器同时集成到 Razor Pages 或 MVC 应用中,则前面的示例将引发 JSException。 如果导航到 Client 项目的 Razor 组件之一,或者导航到具有嵌入组件的 Server 的页面或视图,会引发一个或多个 JSException

这是正常行为,因为预呈现和集成具有可路由 Razor 组件的 Blazor WebAssembly 应用与使用 CSS 选择器不兼容。

如果你一直在使用前面部分中的示例,并且只想看到 CSS 选择器在示例应用中工作,请注释掉 Client 项目 Program.cs 文件的 App 根组件的规范:

- builder.RootComponents.Add<App>("#app");
+ //builder.RootComponents.Add<App>("#app");

通过使用 CSS 选择器的嵌入式 Razor 组件(例如前面示例的 /razorpagescounter2)导航到页面或视图。 页面或视图随嵌入式组件一起加载,而嵌入式组件按预期运行。

保留预呈现状态

在不保留预呈现状态的情况下,在预呈现期间使用的状态将丢失,并且在完全加载应用时必须重新创建。 如果任何状态都是异步设置的,则 UI 可能会闪烁,因为预呈现 UI 将替换为临时占位符,然后再次完全呈现。

为了解决这些问题,Blazor 使用预留组件状态标记帮助程序在预呈现页面中支持持久化状态。 在结束 </body> 标记中添加标记帮助程序的标记 <persist-component-state />

Pages/_Layout.cshtml:

<body>
    ...

    <persist-component-state />
</body>

决定要使用 PersistentComponentState 服务保留的状态。 PersistentComponentState.RegisterOnPersisting 注册回调以在暂停应用之前保留组件状态。 在应用程序恢复时检索状态。

以下示例是基于 Blazor 项目模板的托管的 Blazor WebAssembly 应用中 FetchData 组件的更新版本。 WeatherForecastPreserveState 组件在预呈现期间保留天气预报状态,然后检索状态以初始化组件。 保留组件状态标记帮助程序在所有组件调用之后保留组件状态。

Pages/WeatherForecastPreserveState.razor:

@page "/weather-forecast-preserve-state"
@implements IDisposable
@using BlazorSample.Shared
@inject IWeatherForecastService WeatherForecastService
@inject PersistentComponentState ApplicationState

<PageTitle>Weather Forecast</PageTitle>

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[] forecasts = Array.Empty<WeatherForecast>();
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistForecasts);

        if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
            "fetchdata", out var restored))
        {
            forecasts = 
                await WeatherForecastService.GetForecastAsync(DateTime.Now);
        }
        else
        {
            forecasts = restored!;
        }
    }

    private Task PersistForecasts()
    {
        ApplicationState.PersistAsJson("fetchdata", forecasts);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

通过使用在预呈现期间使用的相同状态来初始化组件,将只执行一次成本高昂的初始化步骤。 呈现的 UI 也与预呈现 UI 相匹配,因此浏览器不会闪烁。

持久预呈现状态将传输到客户端,用于还原组件状态。 对于托管 Blazor WebAssembly 应用中的预呈现,数据将公开给浏览器,并且不得包含敏感的私有信息。

其他 Blazor WebAssembly 资源

预呈现通过呈现搜索引擎可用来计算网页排名的初始 HTTP 响应的内容,可以改进搜索引擎优化 (SEO)

解决方案配置

预呈现配置

若要设置托管 Blazor WebAssembly 应用的预呈现,请执行以下操作:

  1. 将 Blazor WebAssembly 应用托管在 ASP.NET Core 应用中。 可以将独立的 Blazor WebAssembly 应用添加到 ASP.NET Core 解决方案中,也可以使用通过托管选项从 Blazor WebAssembly 项目模板创建的托管 Blazor WebAssembly 应用:

    • Visual Studio:在“其他信息”对话框中,创建 Blazor WebAssembly 应用时选中“托管的 ASP.NET Core”复选框。 在本文的示例中,该解决方案的名称为 BlazorHosted
    • Visual Studio Code/.NET CLI 命令行界面:dotnet new blazorwasm -ho(使用 -ho|--hosted 选项)。 使用 -o|--output {LOCATION} 选项为解决方案创建文件夹,并设置解决方案的项目命名空间。 在本文的示例中,该解决方案的名称为 BlazorHosted (dotnet new blazorwasm -ho -o BlazorHosted)。

    对于本文中的示例,客户端项目的命名空间为 BlazorHosted.Client,服务器项目的命名空间为 BlazorHosted.Server

  2. 从 Blazor WebAssemblyClient 项目中删除 wwwroot/index.html 文件。

  3. Client 项目中,删除 Program.cs 中的以下行:

    - builder.RootComponents.Add<App>("#app");
    
  4. Pages/_Host.cshtml 文件添加到 Server 项目的 Pages 文件夹。 可以使用命令行界面中的 dotnet new blazorserver -o BlazorServer 命令获得 Blazor Server 模板创建的项目的 _Host.cshtml 文件(-o BlazorServer 选项为项目创建文件夹)。 将 Pages/_Host.cshtml 文件放入托管的 Blazor WebAssembly 解决方案的 Server 项目后,对此文件进行以下更改:

    • Client 项目提供 @using 指令(例如 @using BlazorHosted.Client)。

    • 更新样式表链接,以指向 WebAssembly 项目的样式表。 在下面的示例中,客户端项目的命名空间为 BlazorHosted.Client

      - <link href="css/site.css" rel="stylesheet" />
      - <link href="_content/BlazorServer/_framework/scoped.styles.css" rel="stylesheet" />
      + <link href="css/app.css" rel="stylesheet" />
      + <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />
      

      注意

      就地保留请求启动样式 (css/bootstrap/bootstrap.min.css) 表的 <link> 元素。

    • 更新组件标记帮助程序render-mode,以使用 WebAssemblyPrerendered 预呈现根 App 组件:

      - <component type="typeof(App)" render-mode="ServerPrerendered" />
      + <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
      
    • 更新 Blazor 脚本源,以使用客户端 Blazor WebAssembly 脚本:

      - <script src="_framework/blazor.server.js"></script>
      + <script src="_framework/blazor.webassembly.js"></script>
      
  5. Server 项目的 Startup.Configure 中,将回退从 index.html 文件更改为 _Host.cshtml 页面。

    Startup.cs:

    - endpoints.MapFallbackToFile("index.html");
    + endpoints.MapFallbackToPage("/_Host");
    
  6. 如果 ClientServer 项目在预呈现期间使用一个或多个通用服务,则将服务注册分解为可以从这两个项目调用的方法。 有关详细信息,请参阅 ASP.NET Core Blazor 依赖项注入

  7. 运行 Server 项目。 托管 Blazor WebAssembly 应用由客户端的 Server 项目预呈现。

用于将 Razor 组件嵌入页面或视图的配置

本文中的以下部分和示例,用于将客户端Blazor WebAssembly应用的组件嵌入Razor服务器应用的页面或视图需要其他配置。

使用 Server 项目中的默认 Razor Pages 或 MVC 布局文件。 Server 项目必须具有以下文件和文件夹。

Razor Pages:

  • Pages/Shared/_Layout.cshtml
  • Pages/_ViewImports.cshtml
  • Pages/_ViewStart.cshtml

MVC:

  • Views/Shared/_Layout.cshtml
  • Views/_ViewImports.cshtml
  • Views/_ViewStart.cshtml

从 Razor Pages 或 MVC 项目模板创建的应用获取上述文件。 有关详细信息,请参阅教程:在 ASP.NET Core 中开始使用 Razor Pages开始使用 ASP.NET Core MVC

更新导入的 _ViewImports.cshtml 文件中的命名空间,使其与接收文件的 Server 项目所使用的命名空间相匹配。

更新导入的布局文件 (_Layout.cshtml) 以包含 Client 项目的样式。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client。 可以同时更新的 <title> 元素。

Pages/Shared/_Layout.cshtml (Razor Pages) 或 Views/Shared/_Layout.cshtml (MVC):

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-   <title>@ViewData["Title"] - DonorProject</title>
+   <title>@ViewData["Title"] - BlazorHosted</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
+   <link href="css/app.css" rel="stylesheet" />
+   <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />
</head>

导入的布局包含 HomePrivacy 导航链接。 若要使 Home 链接指向托管 Blazor WebAssembly 应用,请更改超链接:

- <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>

在 MVC 布局文件中:

- <a class="nav-link text-dark" asp-area="" asp-controller="Home" 
-     asp-action="Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>

若要使 Privacy 链接指向 privacy 页,请将 privacy 页添加到 Server 项目。

Server 项目中的 Pages/Privacy.cshtml

@page
@model BlazorHosted.Server.Pages.PrivacyModel
@{
}

<h1>Privacy Policy</h1>

如果首选基于 MVC 的 privacy 视图,请在 Server 项目中创建 privacy 视图。

View/Home/Privacy.cshtml:

@{
    ViewData["Title"] = "Privacy Policy";
}

<h1>@ViewData["Title"]</h1>

Home 控制器中,返回视图。

Controllers/HomeController.cs:

public IActionResult Privacy()
{
    return View();
}

从赞助商项目的 wwwroot 文件夹中将静态资产导入到 Server 项目:

  • wwwroot/css 文件夹和内容
  • wwwroot/js 文件夹和内容
  • wwwroot/lib 文件夹和内容

如果从 ASP.NET Core 项目模板创建了赞助商项目,但未修改文件,则可以将整个 wwwroot 文件夹从赞助商项目复制到 Server 项目中,并删除 favicon 图标文件。

警告

避免将静态资产放入 ClientServer wwwroot 文件夹中。 如果这两个文件夹中存在相同文件,则会引发异常,因为每个文件夹中的静态资产共享同一个 Web 根路径。 因此,在任意一个 wwwroot 文件夹(而不是两者)中托管静态资产。

使用组件标记帮助程序,通过页面或视图呈现组件

配置解决方案(包括其他配置)后,组件标记帮助程序支持两种呈现模式来通过页面或视图呈现 Blazor WebAssembly 应用中的组件:

在下面的 Razor Pages 示例中,页面中呈现了 Counter 组件。 页面的呈现部分包含了 Blazor WebAssembly 脚本,以使组件成为交互式组件。 为客户端项目的 Pages 命名空间添加 @using 指令,以避免将 Counter 组件的整个命名空间用于组件标记帮助程序 ({ASSEMBLY NAME}.Pages.Counter)。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client

Server 项目中,Pages/RazorPagesCounter1.cshtml

@page
@using BlazorHosted.Client.Pages

<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" />

@section Scripts {
    <script src="_framework/blazor.webassembly.js"></script>
}

运行 Server 项目。 导航到 /razorpagescounter1 中的 Razor 页。 预呈现的 Counter 组件嵌入在页面中。

RenderMode 配置组件是否:

  • 在页面中预呈现。
  • 在页面上呈现为静态 HTML,或者包含从用户代理启动 Blazor 应用所需的信息。

有关组件标记帮助程序的详细信息(包括传递参数和 RenderMode 配置),请参阅 ASP.NET Core 中的组件标记帮助程序

可能需要额外的工作,具体取决于组件使用的静态资源以及布局页面在应用中的组织方式。 通常,脚本添加到了页面或视图的 Scripts 呈现部分,样式表添加到了布局的 <head> 元素内容。

使用 CSS 选择器,通过页面或视图呈现组件

配置解决方案(包括其他配置)后,将根组件添加到 Program.cs 中托管的 Blazor WebAssembly 解决方案的 Client 项目中。 下面的示例使用 CSS 选择器将 Counter 组件声明为根组件,该选择器会选择 idcounter-component 匹配的元素。 在下面的示例中,Client 项目的命名空间为 BlazorHosted.Client

Client 项目的 Program.cs 中,将项目的 Razor 组件的命名空间添加到文件顶部:

using BlazorHosted.Client.Pages;

Program.cs 中建立 builder 后,将 Counter 组件添加为根组件:

builder.RootComponents.Add<Counter>("#counter-component");

在下面的 Razor Pages 示例中,页面中呈现了 Counter 组件。 页面的呈现部分包含了 Blazor WebAssembly 脚本,以使组件成为交互式组件。

Server 项目中,Pages/RazorPagesCounter2.cshtml

@page

<div id="counter-component">Loading...</div>

@section Scripts {
    <script src="_framework/blazor.webassembly.js"></script>
}

运行 Server 项目。 导航到 /razorpagescounter2 中的 Razor 页。 预呈现的 Counter 组件嵌入在页面中。

可能需要额外的工作,具体取决于组件使用的静态资源以及布局页面在应用中的组织方式。 通常,脚本添加到了页面或视图的 Scripts 呈现部分,样式表添加到了布局的 <head> 元素内容。

注意

如果 Blazor WebAssembly 应用已预呈现,并且使用 CSS 选择器同时集成到 Razor Pages 或 MVC 应用中,则前面的示例将引发 JSException。 导航到 Client 项目的 Razor 组件之一会引发以下异常:

Microsoft.JSInterop.JSException:找不到与选择器“#counter-component”匹配的任何元素。

这是正常行为,因为预呈现和集成具有可路由 Razor 组件的 Blazor WebAssembly 应用与使用 CSS 选择器不兼容。

其他 Blazor WebAssembly 资源

.NET 5 或更高版本的 ASP.NET Core 支持将 Razor 组件集成到 Razor 托管 Blazor WebAssembly解决方案 中的 Pages 或 MVC 应用中。 请选择本文的 .NET 5 或更高版本。