使用 Razor 类库 (RCL) 中的 ASP.NET Core Razor 组件

注意

此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

对于当前版本,请参阅此文的 .NET 8 版本

组件可在 Razor 类库 (RCL) 中跨项目共享。 在应用中包含组件和静态资产,它们来自:

  • 解决方案中的另一个项目。
  • 引用的 .NET 库。
  • NuGet 程序包。

正如组件是常规的 .NET 类型一样,RCL 提供的组件也是普通的 .NET 程序集。

创建 RCL

  1. 创建新项目。
  2. 在“新建项目”对话框中,从 ASP.NET Core 项目模板列表中选择“Razor 类库” 。 选择“下一步” 。
  3. 在“配置新项目”对话框的“项目名称”字段中提供项目名称,或接受默认项目名称 。 本主题中的示例使用项目名称 ComponentLibrary。 选择“创建”。
  4. 在“创建新的 Razor 类库”对话框中,选择“创建” 。
  5. 将 RCL 添加到一个解决方案:
    1. 打开解决方案。
    2. 在解决方案资源管理器中,右击解决方案。 选择“添加”>“现有项目” 。
    3. 导航到 RCL 的项目文件。
    4. 选择 RCL 的项目文件 (.csproj)。
  6. 从应用中添加对 RCL 的引用:
    1. 右键单击该应用项目。 选择“添加”>“项目引用”。
    2. 选择 RCL 项目。 选择“确定”。

如果在从模板生成 RCL 时选中了“支持页和视图”复选框以支持页面和视图,则执行以下操作:

  • 使用以下内容,在生成的 RCL 项目的根中添加 _Imports.razor 文件,以便能够执行 Razor 组件创作:

    @using Microsoft.AspNetCore.Components.Web
    
  • 在项目文件 (.csproj) 中添加以下 SupportedPlatform 项:

    <ItemGroup>
      <SupportedPlatform Include="browser" />
    </ItemGroup>
    

    有关 SupportedPlatform 项的详细信息,请参阅客户端浏览器兼容性分析器部分。

如果在从模板生成 RCL 时选中了“支持页面和视图”复选框,以支持页面和视图,则使用以下内容,在生成的 RCL 项目的根中添加 _Imports.razor 文件,以便能够执行 Razor 组件创作:

@using Microsoft.AspNetCore.Components.Web

使用 RCL 中的 Razor 组件

若要在其他项目中使用 RCL 中的组件,则使用以下方法之一:

  • 使用包含 RCL 命名空间的完整组件类型名称。
  • 如果 Razor 的 @using 指令声明了 RCL 的命名空间,则可以使用不含 RCL 命名空间的名称添加各个组件。 使用以下方法:
    • @using 指令添加到各个组件。
    • 在顶级 _Imports.razor 文件中包含 @using 指令,使库的组件可用于整个项目。 将指令添加到任何级别的 _Imports.razor 文件,将命名空间应用于文件夹中的单个组件或一组组件。 使用 _Imports.razor 文件时,各个组件不需要包含表示 RCL 命名空间的 @using 指令。

在下面的示例中,ComponentLibrary 是包含 Component1 组件的 RCL。 如果从 RCL 项目模板创建的是不支持页面和视图 RCL,则示例组件 Component1 组件会自动添加到其中。

注意

如果创建的 RCL 支持页面和视图,则在 RCL 中手动添加 Component1 组件及其静态资产,以便能够按照本文中的示例操作。 组件和静态资产如以下部分中所示。

ComponentLibrary RCL 中的 Component1.razor

<div class="my-component">
    This component is defined in the <strong>ComponentLibrary</strong> package.
</div>

在使用 RCL 的应用中,使用其命名空间引用 Component1 组件,如下面的示例所示。

ConsumeComponent1.razor

@page "/consume-component-1"

<h1>Consume component (full namespace example)</h1>

<ComponentLibrary.Component1 />

或者,不使用其命名空间,而是添加一个 @using 指令以使用该组件。 以下 @using 指令还可以出现在当前文件夹中或其上方的任何 _Imports.razor 文件中。

ConsumeComponent2.razor

@page "/consume-component-2"
@using ComponentLibrary

<h1>Consume component (<code>@@using</code> example)</h1>

<Component1 />

如果库组件使用 CSS 隔离,组件样式会自动用于使用库的应用。 无需在使用该库的应用中手动链接或导入库的各个组件样式表或其捆绑的 CSS 文件。 应用使用 CSS 导入来引用 RCL 的捆绑样式。 捆绑样式不会作为使用该库的应用的静态 Web 资产发布。 对于名为 ClassLib 的类库和具有 BlazorSample.styles.css 样式表的 Blazor 应用,RCL 的样式表会在生成时自动导入到应用样式表的顶部:

@import '_content/ClassLib/ClassLib.bundle.scp.css';

在前面的示例中,Component1 的样式表 (Component1.razor.css) 会自动捆绑。

ComponentLibrary RCL 中的 Component1.razor.css

.my-component {
    border: 2px dashed red;
    padding: 1em;
    margin: 1em 0;
    background-image: url('background.png');
}

还会包含 RCL 项目模板中的背景图像,并将其保留在 RCL 的 wwwroot 文件夹中。

ComponentLibrary RCL 中的 wwwroot/background.png

来自 RCL 项目模板的带斜条纹的背景图像

若要在库的 wwwroot 文件夹中的样式表中提供更多库组件样式,请向 RCL 的使用者添加样式表 <link> 标记,如下一个示例所示。

重要

通常,库组件使用 CSS 隔离来捆绑和提供组件样式。 依赖于 CSS 隔离的组件样式会自动提供给使用 RCL 的应用。 无需在使用该库的应用中手动链接或导入库的各个组件样式表或其捆绑的 CSS 文件。 以下示例在不使用 CSS 隔离的情况下提供全局样式表,这通常不是使用 RCL 的典型应用的要求。

下一个示例中使用了以下背景图像。 实现此部分中所示的示例后,请右键单击该图像以将其保存在本地。

ComponentLibrary RCL 中的 wwwroot/extra-background.png

开发人员添加到库中的带斜条纹的背景图像

使用 extra-style 类向 RCL 添加新的样式表。

ComponentLibrary RCL 中的 wwwroot/additionalStyles.css

.extra-style {
    border: 2px dashed blue;
    padding: 1em;
    margin: 1em 0;
    background-image: url('extra-background.png');
}

将组件添加到使用 extra-style 类的 RCL。

ComponentLibrary RCL 中的 ExtraStyles.razor

<div class="extra-style">
    <p>
        This component is defined in the <strong>ComponentLibrary</strong> package.
    </p>
</div>

在使用 RCL 的 ExtraStyles 组件的应用中添加一个页面。

ConsumeComponent3.razor

@page "/consume-component-3"
@using ComponentLibrary

<h1>Consume component (<code>additionalStyles.css</code> example)</h1>

<ExtraStyles />

在应用程序的 <head> 标记中链接到库的样式表(<head> 内容的位置)。

<link href="_content/ComponentLibrary/additionalStyles.css" rel="stylesheet" />

如果库组件使用 CSS 隔离,组件样式会自动用于使用库的应用。 无需在使用该库的应用中手动链接或导入库的各个组件样式表或其捆绑的 CSS 文件。 应用使用 CSS 导入来引用 RCL 的捆绑样式。 捆绑样式不会作为使用该库的应用的静态 Web 资产发布。 对于名为 ClassLib 的类库和具有 BlazorSample.styles.css 样式表的 Blazor 应用,RCL 的样式表会在生成时自动导入到应用样式表的顶部:

@import '_content/ClassLib/ClassLib.bundle.scp.css';

在前面的示例中,Component1 的样式表 (Component1.razor.css) 会自动捆绑。

ComponentLibrary RCL 中的 Component1.razor.css

.my-component {
    border: 2px dashed red;
    padding: 1em;
    margin: 1em 0;
    background-image: url('background.png');
}

还会包含 RCL 项目模板中的背景图像,并将其保留在 RCL 的 wwwroot 文件夹中。

ComponentLibrary RCL 中的 wwwroot/background.png

来自 RCL 项目模板的带斜条纹的背景图像

RCL 的示例组件 Component1 使用以下背景图像和样式表。 无需将这些静态资产添加到从 RCL 项目模板创建的新 RCL,因为项目模板会自动添加它们。

ComponentLibrary RCL 中的 wwwroot/background.png

RCL 项目模板添加到库中的带斜条纹的背景图像

ComponentLibrary RCL 中的 wwwroot/styles.css

.my-component {
    border: 2px dashed red;
    padding: 1em;
    margin: 1em 0;
    background-image: url('background.png');
}

若要提供 Component1my-component CSS 类,请在应用的 <head> 标记(<head> 内容的位置)中链接到库的样式表。

<link href="_content/ComponentLibrary/styles.css" rel="stylesheet" />

使可路由组件可从 RCL 获取

若要使 RCL 中的可路由组件可用于直接请求,必须向应用的路由器公开 RCL 的程序集。

打开应用的 App 组件 (App.razor)。 将 Assembly 集合分配给 Router 组件的 AdditionalAssemblies 参数以包括 RCL 的程序集。 在以下示例中,ComponentLibrary.Component1 组件用于发现 RCL 的程序集。

AdditionalAssemblies="new[] { typeof(ComponentLibrary.Component1).Assembly }"

有关详细信息,请参阅 ASP.NET Core Blazor 路由和导航

wwwroot 文件夹中创建具有静态资产的 RCL

RCL 的静态资产可用于任何使用该库的应用。

将静态资产放在 RCL 的 wwwroot 文件夹中,并在应用中使用以下路径引用静态资产:_content/{PACKAGE ID}/{PATH AND FILE NAME}{PACKAGE ID} 占位符是库的包 ID。 如果项目文件中没有指定 <PackageId>,则包 ID 默认为项目的程序集名称。 {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。 此路径格式还用于应用中由添加到 RCL 的 NuGet 包提供的静态资产。

下面的示例演示了如何使用名为 ComponentLibrary 的 RCL 和使用该 RCL 的 Blazor 应用使用 RCL 静态资产。 此应用包含对 ComponentLibrary RCL 的项目引用。

本部分的示例中使用了下面的 Jeep® 图像。 实现此部分中所示的示例后,请右键单击该图像以将其保存在本地。

ComponentLibrary RCL 中的 wwwroot/jeep-yj.png

Jeep YJ®

在 RCL 中添加以下 JeepYJ 组件。

ComponentLibrary RCL 中的 JeepYJ.razor

<h3>ComponentLibrary.JeepYJ</h3>

<p>
    <img alt="Jeep YJ&reg;" src="_content/ComponentLibrary/jeep-yj.png" />
</p>

在使用 ComponentLibrary RCL 的应用中添加以下 Jeep 组件。 Jeep 组件使用:

  • ComponentLibrary RCL 的 wwwroot 文件夹中的 Jeep YJ® 图像。
  • RCL 中的 JeepYJ 组件。

Jeep.razor

@page "/jeep"
@using ComponentLibrary

<div style="float:left;margin-right:10px">
    <h3>Direct use</h3>

    <p>
        <img alt="Jeep YJ&reg;" src="_content/ComponentLibrary/jeep-yj.png" />
    </p>
</div>

<JeepYJ />

<p>
    <em>Jeep</em> and <em>Jeep YJ</em> are registered trademarks of 
    <a href="https://www.stellantis.com">FCA US LLC (Stellantis NV)</a>.
</p>

呈现的 Jeep 组件:

Jeep 组件

有关详细信息,请参阅使用 ASP.NET Core 的类库中的可重用 Razor UI

使用与组件并置的 JavaScript 文件创建 RCL

为 Razor 组件并置 JavaScript (JS) 文件是整理应用中脚本的简便方法。

Blazor 应用的 Razor 组件使用 .razor.js 扩展名并置 JS 文件,并且可通过项目中文件的路径公开寻址:

{PATH}/{COMPONENT}.{EXTENSION}.js

  • 占位符 {PATH} 是指向组件的路径。
  • 占位符 {COMPONENT} 是组件。
  • 占位符 {EXTENSION} 与组件(razor)的扩展名匹配。

发布应用后,框架会自动将脚本移动到 Web 根目录。 脚本将移动到 bin/Release/{TARGET FRAMEWORK MONIKER}/publish/wwwroot/{PATH}/Pages/{COMPONENT}.razor.js,其中占位符为:

无需更改脚本的相对 URL,因为 Blazor 会为你将文件 JS 放置在已发布的静态资产中。

本部分和以下示例主要侧重于说明 JS 文件并置。 第一个示例演示了具有普通 JS 函数的 JS 并置文件。 第二个示例演示了如何使用模块加载函数,这是建议用于大多数生产应用的方法。 ASP.NET Core 中从 .NET 调用 JavaScript 函数的方法Blazor中全面介绍了从 .NET 调用 JS,其中还详细介绍了 BlazorJS API 并提供了一些额外示例。 ASP.NET Core Razor 组件生命周期中介绍了第二个示例中出现的组件处置。

以下 JsCollocation1 组件通过 HeadContent 组件加载脚本,并使用 IJSRuntime.InvokeAsync 调用函数 JS。 占位符 {PATH} 是指向组件的路径。

重要

如果在测试应用中使用以下代码进行演示,请将 {PATH} 占位符更改为组件的路径(例如:在 .NET 8 或更高版本中为 Components/Pages,在 .NET 7 或更低版本中为 Pages)。 在 Blazor Web 应用(.NET 8 或更高版本)中,组件需要全局应用于相应应用或组件定义的交互式呈现模式。

在脚本 Blazor 后添加以下脚本(启动脚本 Blazor 的位置):

<script src="{PATH}/JsCollocation1.razor.js"></script>

JsCollocation1 组件 ({PATH}/JsCollocation1.razor):

@page "/js-collocation-1"
@inject IJSRuntime JS

<PageTitle>JS Collocation 1</PageTitle>

<h1>JS Collocation Example 1</h1>

<button @onclick="ShowPrompt">Call showPrompt1</button>

@if (!string.IsNullOrEmpty(result))
{
    <p>
        Hello @result!
    </p>
}

@code {
    private string? result;

    public async void ShowPrompt()
    {
        result = await JS.InvokeAsync<string>(
            "showPrompt1", "What's your name?");
        StateHasChanged();
    }
}

并置的 JS 文件放置在文件名为 JsCollocation1.razor.jsJsCollocation1 组件文件旁边。 在 JsCollocation1 组件中,在并置文件的路径中引用了该脚本。 在以下示例中,该 showPrompt1 函数接受来自 Window prompt() 的用户名称,并将其返回到组件 JsCollocation1 以进行显示。

{PATH}/JsCollocation1.razor.js

function showPrompt1(message) {
  return prompt(message, 'Type your name here');
}

不建议在生产应用中使用上述方法,因为它会污染具有全局功能的客户端。 生产应用适用的更好方法是使用 JS 模块。 相同的普遍原则适用于从并置的 JS 文件加载 JS 模块,如下一个示例所示。

以下 JsCollocation2 组件的 OnAfterRenderAsync 方法将 JS 模块加载到 module,这是组件类的一个 IJSObjectReferencemodule 用于调用 showPrompt2 函数。 占位符 {PATH} 是指向组件的路径。

重要

如果在测试应用中使用以下代码进行演示,请将占位符 {PATH} 更改为组件的路径。 在 Blazor Web 应用(.NET 8 或更高版本)中,组件需要全局应用于相应应用或组件定义的交互式呈现模式。

JsCollocation2 组件 ({PATH}/JsCollocation2.razor):

@page "/js-collocation-2"
@implements IAsyncDisposable
@inject IJSRuntime JS

<PageTitle>JS Collocation 2</PageTitle>

<h1>JS Collocation Example 2</h1>

<button @onclick="ShowPrompt">Call showPrompt2</button>

@if (!string.IsNullOrEmpty(result))
{
    <p>
        Hello @result!
    </p>
}

@code {
    private IJSObjectReference? module;
    private string? result;

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            /*
                Change the {PATH} placeholder in the next line to the path of
                the collocated JS file in the app. Examples:

                ./Components/Pages/JsCollocation2.razor.js (.NET 8 or later)
                ./Pages/JsCollocation2.razor.js (.NET 7 or earlier)
            */
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./{PATH}/JsCollocation2.razor.js");
        }
    }

    public async void ShowPrompt()
    {
        if (module is not null)
        {
            result = await module.InvokeAsync<string>(
                "showPrompt2", "What's your name?");
            StateHasChanged();
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

{PATH}/JsCollocation2.razor.js

export function showPrompt2(message) {
  return prompt(message, 'Type your name here');
}

对于 Razor 类库 (RCL) 提供的脚本或模块,使用以下路径:

_content/{PACKAGE ID}/{PATH}/{COMPONENT}.{EXTENSION}.js

  • 占位符 {PACKAGE ID} 是 RCL 的包标识符(或由应用引用的类库的库名称)。
  • 占位符 {PATH} 是指向组件的路径。 如果 Razor 组件位于 RCL 的根目录下,则不包括路径段。
  • 占位符 {COMPONENT} 是组件名称。
  • 占位符 {EXTENSION} 与组件的扩展名匹配,为 razorcshtml

在以下 Blazor 应用示例中:

  • RCL 的包标识符是 AppJS
  • 将为 JsCollocation3 组件 (JsCollocation3.razor) 加载模块的脚本。
  • JsCollocation3 组件位于 RCL 的 Components/Pages 文件夹中。
module = await JS.InvokeAsync<IJSObjectReference>("import", 
    "./_content/AppJS/Components/Pages/JsCollocation3.razor.js");

向多个托管的 Blazor 应用提供组件和静态资产

有关详细信息,请参阅多个托管的 ASP.NET Core Blazor WebAssembly 应用

客户端浏览器兼容性分析器

客户端应用面向整个 .NET API 外围应用,但由于浏览器沙盒约束,并非所有 .NET API 在 WebAssembly 上都受支持。 在 WebAssembly 上运行时,不支持的 API 将引发 PlatformNotSupportedException。 当应用使用应用目标平台不支持的 API 时,平台兼容性分析器会向开发人员发出警告。 对于客户端应用,这意味着需要检查浏览器是否支持这些 API。 为兼容性分析器注释 .NET Framework API 是一个持续的过程,因此并不是所有的 .NET Framework API 当前都已进行注释。

启用交互式 WebAssembly 组件的 Blazor Web 应用、Blazor WebAssembly 应用和 RCL 项目会使用 SupportedPlatform MSBuild 项将 browser 添加为支持的平台,以自动启用浏览器兼容性检查。 库开发人员可以手动将 SupportedPlatform 项添加到库的项目文件以启用该功能:

<ItemGroup>
  <SupportedPlatform Include="browser" />
</ItemGroup>

编写库时,通过将 browser 指定为 UnsupportedOSPlatformAttribute 来指示浏览器不支持特定的 API:

using System.Runtime.Versioning;

...

[UnsupportedOSPlatform("browser")]
private static string GetLoggingDirectory()
{
    ...
}

有关详细信息,请参阅在特定平台(dotnet/designs 存储库)上将 API 注释为不受支持

JavaScript 模块中的 JavaScript 隔离

Blazor 在标准 JavaScript 模块中启用 JavaScript 隔离。 JavaScript 隔离具有以下优势:

  • 导入的 JavaScript 不再污染全局命名空间。
  • 库和组件的使用者不需要手动导入相关的 JavaScript。

有关详细信息,请参阅从 ASP.NET Core Blazor 中的 .NET 方法调用 JavaScript 函数

避免剪裁 JavaScript 可调用的 .NET 方法

运行时重新链接会剪裁类实例 JavaScript 可调用的 .NET 方法,除非这些方法被显式保留。 有关详细信息,请参阅在 ASP.NET Core Blazor 中从 JavaScript 函数调用 .NET 方法

生成并打包库,再将其传送到 NuGet

由于包含 Razor 组件的 Razor 类库是标准的 .NET 库,因此将它们打包并传送到 NuGet 与将任何库打包并传送到 NuGet 没有什么区别。 在命令行界面中使用 dotnet pack 命令,执行打包操作:

dotnet pack

在命令行界面中使用 dotnet nuget push 命令,将包上传到 NuGet。

商标

Jeep 和 Jeep YJ 是 FCA 美国有限责任公司 (Stellantis NV) 的注册商标商标。

其他资源