百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

C# 13 和 .NET 9 全知道 :14 使用 Blazor 构建交互式 Web 组件 (1)

zhezhongyun 2025-01-19 01:48 67 浏览

本章介绍如何使用 Blazor 构建交互式网页用户界面组件。您将学习如何构建可以在网页服务器或网页浏览器中执行其 C# 和 .NET 代码的 Blazor 组件。

当组件在服务器上执行时,Blazor 使用 SignalR 将必要的更新传达给浏览器中的用户界面。

当组件在浏览器中使用 WebAssembly 执行时,它们必须进行 HTTP 调用以与服务器上的数据进行交互。您将在第 15 章《构建和使用 Web 服务》中了解更多信息。

在本章中,我们将涵盖以下主题:

  • 审查 Blazor Web 应用程序项目模板
  • 使用 Blazor 构建组件
  • 使用 EditForm 组件定义表单

审查 Blazor Web 应用程序项目模板

在 .NET 8 之前,不同的托管模型有各自的项目模板,例如 Blazor Server 应用、Blazor WebAssembly 应用和 Blazor WebAssembly 空应用。.NET 8 引入了一个统一的项目模板,名为 Blazor Web 应用,以及一个仅客户端的项目模板,重命名为 Blazor WebAssembly 独立应用。除非必须使用旧版 .NET SDK,否则请避免使用其他遗留项目模板。

创建 Blazor Web 应用程序项目

让我们看看 Blazor Web 应用项目的默认模板。大多数情况下,您会发现它与 ASP.NET Core 空模板相同,只是增加了一些关键内容:

  1. 使用您首选的代码编辑器打开 ModernWeb 解决方案,然后添加一个新项目,如下列表所定义:项目模板:Blazor Web 应用 / blazor --interactivity Auto解决方案文件和文件夹: ModernWeb项目文件和文件夹: Northwind.Blazor认证类型:无配置为 HTTPS:已选择交互式渲染模式:自动(服务器和 WebAssembly)交互位置:每页/组件包含样本页面:已选择请勿使用顶级语句:已清除

如果您正在使用 VS Code 或 Rider,请在命令提示符或终端中输入以下命令: dotnet new blazor --interactivity Auto -o Northwind.Blazor

良好实践:默认的交互式渲染模式是服务器。我们明确选择了自动,以便在此项目中查看两种渲染模式。我们还选择了包含示例页面,这在实际项目中您可能不希望包含。

  1. 注意创建了两个项目:Northwind.Blazor : 这是主要的 ASP.NET Core 项目,定义并运行静态 SSR、流式传输和服务器端 Blazor 组件。它还引用并托管您的客户端 WebAssembly Blazor 组件。Northwind.Blazor.Client : 这是一个 Blazor WebAssembly 项目,用于您定义的任何客户端组件。在未来,它可能不需要在单独的项目中,但对于 .NET 8 和 .NET 9,它仍然需要。
  2. ModernWeb 文件夹中,在 Directory.Packages.props ,添加一个 <ItemGroup> 以设置服务器端托管的版本号和定义 Blazor WebAssembly 包,如下所示的标记:
<ItemGroup Label="For Blazor.">
  <PackageVersion Include=
    "Microsoft.AspNetCore.Components.WebAssembly.Server"
    Version="9.0.0" />
  <PackageVersion Include=
    "Microsoft.AspNetCore.Components.WebAssembly"
    Version="9.0.0" />
</ItemGroup>
  1. Northwind.Blazor.csproj 中,请注意它与使用 Web SDK 并针对.NET 9 的 ASP.NET Core 项目是相同的。还请注意,它引用了客户端项目。
  2. Northwind.Blazor.csproj 中,删除允许该项目托管 WebAssembly 组件的 Microsoft.AspNetCore.Components.WebAssembly.Server 包的版本号,如下所示:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\Northwind.Blazor.Client\Northwind.Blazor.Client.csproj" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
  </ItemGroup>
</Project>
  1. Northwind.Blazor.Client.csproj 中,请注意它类似于 ASP.NET Core 项目,但使用 Blazor WebAssembly SDK。
  2. Northwind.Blazor.Client.csproj 中,删除允许该项目定义 WebAssembly 组件的 Microsoft.AspNetCore.Components.WebAssembly 包的版本号,如下标记所示:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
    <StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
  </ItemGroup>
</Project>
  1. 构建 Northwind.BlazorNorthwind.Blazor.Client 项目。
  2. Northwind.Blazor 中,请注意 Program.cs 几乎与 ASP.NET Core 项目相同。一个不同之处在于配置服务的部分,它调用了 AddRazorComponents 方法,这在我们的 Northwind.Web 项目中也有。该部分还调用以启用服务器和客户端的交互性,如以下代码中所示的高亮部分:
using Northwind.Blazor.Client.Pages;
using Northwind.Blazor.Components;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
  .AddInteractiveServerComponents()
  .AddInteractiveWebAssemblyComponents();
var app = builder.Build();

请注意配置 HTTP 管道的部分,该部分调用了 MapRazorComponents<App> 方法。这配置了一个根应用程序组件,名称为 App.razor ,如下代码所示:

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
  app.UseWebAssemblyDebugging();
}
else
{
  app.UseExceptionHandler(
    "/Error", createScopeForErrors: true);
  // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
  app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
  .AddInteractiveServerRenderMode()
  .AddInteractiveWebAssemblyRenderMode()
  .AddAdditionalAssemblies(
    typeof(Northwind.Blazor.Client._Imports).Assembly);
app.Run();
  1. Northwind.Blazor 中,请注意 Components 文件夹及其子文件夹,如 LayoutPages ,使用了您在启用 Blazor 组件时在 Northwind.Web 项目中使用的相同命名约定。
  2. Northwind.Blazor.Client 中,在 Program.cs 中,请注意它创建了一个 WebAssemblyHostBuilder 而不是正常的 WebApplication 构建器,如以下代码中突出显示的内容所示:
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
await builder.Build().RunAsync();
  1. Northwind.Blazor.Client 中,在 Pages 文件夹中,请注意有一个名为 Counter.razor 的 Blazor 组件。

审查 Blazor 路由、布局和导航

让我们回顾一下这个 Blazor 项目的路由配置、布局和导航菜单:

  1. Northwind.Blazor 项目文件夹中,在 Components 文件夹内,在 App.razor 中,请注意它定义了基本的 HTML 页面标记,该标记引用了本地的 Bootstrap 副本用于样式,以及一些 Blazor 特定的元素,如下标记中突出显示的内容和标记后面列表中提到的内容:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0 " />
  <base href="/" />
  <link rel="stylesheet"
        href="@Assets["bootstrap/bootstrap.min.css"]" />
  <link rel="stylesheet" href="@Assets["app.css"]" />
  <link rel="stylesheet"
        href="@Assets["Northwind.Blazor.styles.css"]" />
  <ImportMap />
  <link rel="icon" type="image/png" href="favicon.png" />
  <HeadOutlet />
</head>
<body>
  <Routes />
  <script src="_framework/blazor.web.js"></script>
</body>
</html>

在审查前面的标记时,请注意以下事项:

  • 资产通过 ComponentBase.Assets 属性进行引用,该属性解析给定资产的指纹 URL。当您在 Program.cs 中使用 MapStaticAssets 中间件时,应使用此属性。
  • 一个 <ImportMap /> Blazor 组件,用于表示一个导入映射元素 ( <script type="importmap"></script> ),该元素定义了模块脚本的导入映射。您可以通过以下链接了解导入映射: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap。
  • 一个 <HeadOutlet /> Blazor 组件,用于将额外内容注入到 <head> 部分。这是所有 Blazor 项目中可用的内置组件之一。例如,在 Blazor 页面组件中,使用 <PageTitle> 组件来设置网页的 <title>
  • 一个 <Routes /> Blazor 组件,用于在此项目中定义自定义路由。由于该组件是当前项目的一部分,因此开发人员可以完全自定义它,文件名为 Routes.razor
  • 一个用于 blazor.web.js 的脚本块,管理与服务器的通信,以支持 Blazor 的动态功能,例如在后台下载 WebAssembly 组件,并随后从服务器端切换到客户端组件执行。
  1. Components 文件夹中,在 Routes.razor ,请注意 <Router> 为当前项目程序集或 Northwind.Blazor.Client 项目程序集(针对 WebAssembly 组件)中的所有 Blazor 组件启用路由,如果找到匹配的路由,则执行 RouteView ,这将把组件的默认布局设置为 MainLayout 并将任何路由数据参数传递给组件。对于该组件,将聚焦于其中的第一个 <h1> 元素,如以下代码所示:
<Router AppAssembly="@typeof(Program).Assembly"
  AdditionalAssemblies="new[] {
    typeof(Client._Imports).Assembly }">
  <Found Context="routeData">
    <RouteView RouteData="@routeData"
               DefaultLayout="@typeof(Layout.MainLayout)" />
    <FocusOnNavigate RouteData="@routeData" Selector="h1" />
  </Found>
</Router>

Components 文件夹中,在 _Imports.razor ,请注意此文件导入了一些在您所有自定义 Blazor 组件中使用的有用命名空间,如下代码所示:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Northwind.Blazor
@using Northwind.Blazor.Client
@using Northwind.Blazor.Components

Components\Layout 文件夹中,在 MainLayout.razor ,请注意它为侧边栏定义了 <div> ,该侧边栏包含一个由本项目中的 NavMenu.razor 组件文件实现的导航菜单,以及用于内容的 HTML5 元素,如 <main><article> ,并有一个 <div> 来显示未处理的错误,如以下代码所示:

@inherits LayoutComponentBase
<div class="page">
  <div class="sidebar">
    <NavMenu />
  </div>
  <main>
    <div class="top-row px-4">
      <a href="https://learn.microsoft.com/aspnet/core/"
         target="_blank">About</a>
    </div>
    <article class="content px-4">
      @Body
    </article>
  </main>
</div>
<div id="blazor-error-ui" data-nosnippet>
    An unhandled error has occurred.
    <a href="." class="reload">Reload</a>
    <span class="dismiss"></span>
</div>
  1. Components\Layout 文件夹中的 MainLayout.razor.css ,请注意它包含该组件的独立 CSS 样式。由于命名约定,在此文件中定义的样式优先于其他可能影响该组件的样式。

Blazor 组件通常需要提供自己的 CSS 以应用样式或 JavaScript 以执行无法仅通过 C# 完成的活动,例如访问浏览器 API。为了确保这不会与站点级别的 CSS 和 JavaScript 冲突,Blazor 支持 CSS 和 JavaScript 隔离。如果您有一个名为 Home.razor 的组件,只需创建一个名为 Home.razor.css 的 CSS 文件。此文件中定义的样式将覆盖项目中的任何其他样式。

  1. Components\Layout 文件夹中,在 NavMenu.razor ,请注意它有三个菜单项,主页、计数器和天气。这些菜单链接是通过使用名为 NavLink 的组件创建的,如下标记所示:
<div class="top-row ps-3 navbar navbar-dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="">Northwind.Blazor</a>
  </div>
</div>
<input type="checkbox" title="Navigation menu"
       class="navbar-toggler" />
<div class="nav-scrollable" onclick=
     "document.querySelector('.navbar-toggler').click()">
  <nav class="flex-column">
    <div class="nav-item px-3">
      <NavLink class="nav-link" href=""
               Match="NavLinkMatch.All">
        <span class="bi bi-house-door-fill-nav-menu"
              aria-hidden="true"></span> Home
      </NavLink>
    </div>
    <div class="nav-item px-3">
      <NavLink class="nav-link" href="counter">
        <span class="bi bi-plus-square-fill-nav-menu"
              aria-hidden="true"></span> Counter
      </NavLink>
    </div>
    <div class="nav-item px-3">
      <NavLink class="nav-link" href="weather">
        <span class="bi bi-list-nested-nav-menu"
              aria-hidden="true"></span> Weather
      </NavLink>
    </div>
  </nav>
</div>
  1. 请注意, NavMenu.razor 有一个名为 NavMenu.razor.css 的独立样式表。
  2. Components\Pages 文件夹中,在 Home.razor ,请注意它定义了一个设置页面标题的组件,然后渲染一个标题和欢迎消息,如下代码所示:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

Components\Pages 文件夹中,在 Weather.razor ,请注意它定义了一个组件,该组件从注入的依赖天气服务中获取天气预报,然后将其渲染在一个表格中,如以下代码所示:

@page "/weather"
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
	
<h1>Weather</h1>
<p>This component demonstrates showing data.</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;
  protected override async Task OnInitializedAsync()
  {
    // Simulate asynchronous loading to demonstrate streaming rendering
    await Task.Delay(500);
    var startDate = DateOnly.FromDateTime(DateTime.Now);
    var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool",
      "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
    forecasts = Enumerable.Range(1, 5).Select(index =>
      new WeatherForecast
      {
        Date = startDate.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = summaries[Random.Shared.Next(summaries.Length)]
      }).ToArray();
  }
  private class WeatherForecast
  {
    public DateOnly Date { get; set; }
    public int TemperatureC { get; set; }
    public string? Summary { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
  }
}

Northwind.Blazor.Client 项目中,在 Pages 文件夹内,在 Counter.razor 中,请注意定义了一个 Blazor 页面组件,其路由为 /counter ,渲染模式将在服务器和 WebAssembly 之间自动切换,具有一个名为 currentCount 的私有字段,该字段在每次点击按钮时递增,如以下标记所示:

@page "/counter"
@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary"
  @onclick="IncrementCount">Click me</button>
@code {
  private int currentCount = 0;
  private void IncrementCount()
  {
    currentCount++;
  }
}

如何定义可路由页面组件

要创建一个可路由的页面组件,请在组件的 .razor 文件顶部添加 @page 指令,如下所示的标记:

@page "/customers"

前面的代码相当于 Program.cs 中的一个映射端点,如下代码所示:

app.MapGet("/customers", () => ...);

一个页面组件可以有多个 @page 指令来注册多个路由,如以下代码所示:

@page "/weather"
@page "/forecast"

Router 组件专门在其 AppAssembly 参数中扫描程序集,以查找具有 @page 指令的 Blazor 组件,将它们的 URL 路径注册为端点。

在运行时,页面组件与您在 Routes.razor 文件 <RouteView> 组件中指定的任何特定布局合并。默认情况下,Blazor Web App 项目模板将 MainLayout.razor 定义为页面组件的布局。

良好实践:按照惯例,将可路由页面的 Blazor 组件放在 Components\Pages 文件夹中。

相关推荐

Chinese vice premier calls for multilateralism at Davos

DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangdeliveredaspeechatthe...

用C++ Qt手把手打造炫酷汽车仪表盘

一、项目背景与核心价值在车载HMI(人机交互界面)开发领域,虚拟仪表盘是智能座舱的核心组件。本项目基于C++Qt框架实现一个具备专业级效果的时速表模块,涵盖以下技术要点:Qt图形绘制核心机制(QPa...

系列专栏(八):JS的第七种基本类型Symbols

ES6作为新一代JavaScript标准,已正式与广大前端开发者见面。为了让大家对ES6的诸多新特性有更深入的了解,MozillaWeb开发者博客推出了《ES6InDepth》系列文章。CSDN...

MFC界面开发工具BCG v31.1 - 增强功能区、工具箱功能

点击“了解更多”获取工具亲爱的BCGSoft用户,我们非常高兴地宣布BCGControlBarProfessionalforMFC和BCGSuiteforMFCv31.2正式发布!新版本支...

雅居乐上调出售吉隆坡项目保留金,预计亏损扩大至6.64亿元

1月2日,雅居乐集团(03383.HK)发布有关出售一家附属公司股权披露交易的补充公告。此前雅居乐集团曾公告,2023年11月8日(交易时段后),集团子公司AgileRealEstateDeve...

Full text: Address by Vice Premier Ding Xuexiang&#39;s at World Economic Forum Annual Meeting 2025

DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangonTuesdaydeliveredasp...

手机性能好不好 GPU玄学曲线告诉你

前言各位在看测试者对手机进行评测时或许会见过“安卓玄学曲线”,所谓中的安卓玄学曲线真名为“ProfileGPURendering”。大多数情况下,在系统“开发者选项中被称为“GPU显示配置文件”或...

小迈科技 X Hologres:高可用的百亿级广告实时数仓建设

通过本文,我们将会介绍小迈科技如何通过Hologres搭建高可用的实时数仓。一、业务介绍小迈科技成立于2015年1月,是一家致力以数字化领先为优势,实现业务高质量自增长的移动互联网科技公司。始...

vue3新特征和所有的属性,方法汇总及其对应源码分析

vue3新特征汇总与源码分析(备注:vue3使用typescript编写)何为应用?constapp=Vue.createApp({})app就是一个应用。应用的配置和应用的API就是app应用...

China&#39;s stability redefines global trade in a volatile era

ContainersareunloadedatQingdaoPort,eastChina'sShandongProvince,December10,2024.[Photo/X...

QML 实现图片帧渐隐渐显轮播

前言所谓图片帧渐隐渐显轮播就是,一组图片列表,当前图片逐渐改变透明度隐藏,同时下一张图片逐渐改变透明度显示,依次循环,达到渐隐渐显的效果,该效果常用于图片展示,相比左右自动切换的轮播方式来说,这种方式...

前端惊魂夜:我竟在CSS里写出了JavaScript?

凌晨两点,写字楼里只剩下我工位上的一盏孤灯。咖啡杯见底,屏幕的光映在疲惫的眼镜片上。为了实现一个极其复杂的动态渐变效果,我翻遍了MDN文档,试遍了所有已知的CSS技巧,却始终差那么一口气。“要是CSS...

10 个派上用场的 Flutter 小部件

尝试学习一门新语言可能会令人恐惧和厌烦。很多时候,我们希望我们知道早先存在的某些功能。在今天的文章中,我将告诉你我希望早点知道的最方便的颤振小部件。SpacerSpacer创建一个可调整的空白空...

让我的 Flutter 代码整洁 10 倍的 5 种

如果你曾在Flutter中使用过SingleTickerProviderStateMixin来制作动画,猜猜怎么着?你已经使用过Mixin了——恭喜你,你已经处于一段你甚至不知道的关...

daisyUI - 主题漂亮、代码纯净!免费开源的 Tailwind CSS 组件库

漂亮有特色的CSS组件库,组件代码非常简洁,也支持深度定制主题、定制组件,可以搭配Vue/React等框架使用。关于daisyUIdaisyUI是一款极为流行的CSSUI组件库,...