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

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

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

本章介绍如何使用 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 文件夹中。

相关推荐

JPA实体类注解,看这篇就全会了

基本注解@Entity标注于实体类声明语句之前,指出该Java类为实体类,将映射到指定的数据库表。name(可选):实体名称。缺省为实体类的非限定名称。该名称用于引用查询中的实体。不与@Tab...

Dify教程02 - Dify+Deepseek零代码赋能,普通人也能开发AI应用

开始今天的教程之前,先解决昨天遇到的一个问题,docker安装Dify的时候有个报错,进入Dify面板的时候会出现“InternalServerError”的提示,log日志报错:S3_USE_A...

用离散标记重塑人体姿态:VQ-VAE实现关键点组合关系编码

在人体姿态估计领域,传统方法通常将关键点作为基本处理单元,这些关键点在人体骨架结构上代表关节位置(如肘部、膝盖和头部)的空间坐标。现有模型对这些关键点的预测主要采用两种范式:直接通过坐标回归或间接通过...

B 客户端流RPC (clientstream Client Stream)

客户端编写一系列消息并将其发送到服务器,同样使用提供的流。一旦客户端写完消息,它就等待服务器读取消息并返回响应gRPC再次保证了单个RPC调用中的消息排序在客户端流RPC模式中,客户端会发送多个请...

我的模型我做主02——训练自己的大模型:简易入门指南

模型训练往往需要较高的配置,为了满足友友们的好奇心,这里我们不要内存,不要gpu,用最简单的方式,让大家感受一下什么是模型训练。基于你的硬件配置,我们可以设计一个完全在CPU上运行的简易模型训练方案。...

开源项目MessageNest打造个性化消息推送平台多种通知方式

今天介绍一个开源项目,MessageNest-可以打造个性化消息推送平台,整合邮件、钉钉、企业微信等多种通知方式。定制你的消息,让通知方式更灵活多样。开源地址:https://github.c...

使用投机规则API加快页面加载速度

当今的网络用户要求快速导航,从一个页面移动到另一个页面时应尽量减少延迟。投机规则应用程序接口(SpeculationRulesAPI)的出现改变了网络应用程序接口(WebAPI)领域的游戏规则。...

JSONP安全攻防技术

关于JSONPJSONP全称是JSONwithPadding,是基于JSON格式的为解决跨域请求资源而产生的解决方案。它的基本原理是利用HTML的元素标签,远程调用JSON文件来实现数据传递。如果...

大数据Doris(六):编译 Doris遇到的问题

编译Doris遇到的问题一、js_generator.cc:(.text+0xfc3c):undefinedreferenceto`well_known_types_js’查找Doris...

网页内嵌PDF获取的办法

最近女王大人为了通过某认证考试,交了2000RMB,官方居然没有给线下教材资料,直接给的是在线教材,教材是PDF的但是是内嵌在网页内,可惜却没有给具体的PDF地址,无法下载,看到女王大人一点点的截图保...

印度女孩被邻居家客人性骚扰,父亲上门警告,反被围殴致死

微信的规则进行了调整希望大家看完故事多点“在看”,喜欢的话也点个分享和赞这样事儿君的推送才能继续出现在你的订阅列表里才能继续跟大家分享每个开怀大笑或拍案惊奇的好故事啦~话说只要稍微关注新闻的人,应该...

下周重要财经数据日程一览 (1229-0103)

下周焦点全球制造业PMI美国消费者信心指数美国首申失业救济人数值得注意的是,下周一希腊还将举行第三轮总统选举需要谷歌日历同步及部分智能手机(安卓,iPhone)同步日历功能的朋友请点击此链接,数据公布...

PyTorch 深度学习实战(38):注意力机制全面解析

在上一篇文章中,我们探讨了分布式训练实战。本文将深入解析注意力机制的完整发展历程,从最初的Seq2Seq模型到革命性的Transformer架构。我们将使用PyTorch实现2个关键阶段的注意力机制变...

聊聊Spring AI的EmbeddingModel

序本文主要研究一下SpringAI的EmbeddingModelEmbeddingModelspring-ai-core/src/main/java/org/springframework/ai/e...

前端分享-少年了解过iframe么

iframe就像是HTML的「内嵌画布」,允许在页面中加载独立网页,如同在画布上叠加另一幅动态画卷。核心特性包括:独立上下文:每个iframe都拥有独立的DOM/CSS/JS环境(类似浏...