.NET 10 中的 Blazor:新增功能及常见问题

目录

[为什么你的 Blazor 代码即将变得更简单](#为什么你的 Blazor 代码即将变得更简单)

样板问题

[.NET 10:"我们真正修复了状态"版本](#.NET 10:“我们真正修复了状态”版本)

持久状态,不会让你哭泣

[电路状态持久性("我的 WebSocket 连接断开"问题)](#电路状态持久性(“我的 WebSocket 连接断开”问题))

[性能:Blazor .NET 10 基准测试](#性能:Blazor .NET 10 基准测试)

[Blazor .NET 10 中的 JavaScript 互操作](#Blazor .NET 10 中的 JavaScript 互操作)

真正的胜利:制造商的支持

WebAssembly热重载

表单验证源生成器

[404 处理方式合情合理](#404 处理方式合情合理)

导航改进

重新连接界面更新

支持密码认证

[QuickGrid 更新](#QuickGrid 更新)

WebAssembly改进

改进的服务验证

默认响应流

性能诊断

迁移清单

避免常见错误

[1. 过度使用PersistentState](#1. 过度使用[PersistentState])

[2. 忘记新的验证注册](#2. 忘记新的验证注册)

[3. 假设通行密钥在所有地方都有效](#3. 假设通行密钥在所有地方都有效)

[4. 忽略电路暂停/恢复](#4. 忽略电路暂停/恢复)

何时升级

常见问题解答

[.NET 10 是 LTS 版本吗?](#.NET 10 是 LTS 版本吗?)

[Blazor .NET 10 的最大改进是什么?](#Blazor .NET 10 的最大改进是什么?)

[Blazor .NET 10 是否支持密码?](#Blazor .NET 10 是否支持密码?)

[.NET 10 中的 Blazor JavaScript 包体积缩小了多少?](#.NET 10 中的 Blazor JavaScript 包体积缩小了多少?)

[我可以在 .NET 10 中使用 Blazor WebAssembly 的热重载功能吗?](#我可以在 .NET 10 中使用 Blazor WebAssembly 的热重载功能吗?)

[如何从 .NET 9 迁移到 .NET 10?](#如何从 .NET 9 迁移到 .NET 10?)

最后想说的话


如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

探索 Blazor .NET 10 的所有新特性:持久状态管理、改进的 JavaScript 互操作性、密钥身份验证以及显著的性能提升。这是一份面向 .NET 开发人员的全面指南,其中包含代码示例。

为什么你的 Blazor 代码即将变得更简单

Blazor 在 .NET 10 中的出现,标志着微软 Web UI 框架向前迈出了重要一步。我还记得 Blazor 曾经是 .NET 圈子里一个特立独行的孩子。"你想在浏览器里运行 C#?这太可爱了。" 快进到 2025 年,Blazor .NET 10 不再是弱者,而是真正的竞争者。

Blazor 一直都很好用。但有些图案需要比实际需要的更繁琐的步骤。

.NET 10 最终解决了之前存在的一些问题。有关官方概述,请参阅微软的ASP.NET Core 10.0 新特性文档。

样板问题

很多开发者都经历过这种情况:你启动了一个 Blazor 项目,兴致勃勃,编写组件,组件运行正常,一切顺利。

然后你的老板问:"为什么页面在导航时会闪白光?"

然后你发现了预渲染、水合以及双重渲染问题。框架处理了所有这些,但你必须自己编写底层逻辑。手动序列化状态、权衡OnInitializedAsync各种方案OnAfterRenderAsync、构建那些感觉本应内置的抽象层。

六个月后,你的Program.cs

builder.Services.AddScoped<IStateContainer, StateContainer>();

builder.Services.AddScoped<IPreRenderStateService, PreRenderStateService>();

builder.Services.AddScoped<IHydrationHelper, HydrationHelper>();

builder.Services.AddScoped<IComponentStateManager, ComponentStateManager>();

builder.Services.AddScoped<ICircuitHandler, CustomCircuitHandler>();

builder.Services.AddSingleton<IReconnectionStateTracker, ReconnectionStateTracker>();

// Services that worked great, but shouldn't have been your job to write

获取天气数据的组件?它获取了两次。一次是在预渲染阶段,一次是在交互模式下。你的 API 团队肯定恨死你了。

.NET 10:"我们真正修复了状态"版本

让我来向您展示一下新增功能,以及它为何如此重要。

持久状态,不会让你哭泣

.NET 10 之前:

@inject PersistentComponentState ApplicationState

@code {

private WeatherForecast\[\]? forecasts;

private PersistingComponentStateSubscription _subscription;

protected override async Task OnInitializedAsync()

{

_subscription = ApplicationState.RegisterOnPersisting(PersistData);

if (!ApplicationState.TryTakeFromJson<WeatherForecast\[\]>("forecasts", out forecasts))

{

forecasts = await FetchForecasts();

}

}

private Task PersistData()

{

ApplicationState.PersistAsJson("forecasts", forecasts);

return Task.CompletedTask;

}

public void Dispose()

{

_subscription.Dispose();

}

}

仅仅为了避免重复获取数据,就需要 25 行繁琐的代码。每个组件都是如此。

.NET 10 之后:

@code {

PersistentState

public WeatherForecast\[\]? Forecasts { get; set; }

protected override async Task OnInitializedAsync()

{

Forecasts ??= await FetchForecasts();

}

}

一个属性。就这么简单。运行时会处理序列化、数据填充和清理。你的 API 团队甚至可能再次邀请你共进午餐。在官方文档中了解更多关于Blazor 状态管理的信息。

电路状态持久性("我的 WebSocket 连接断开"问题)

你肯定遇到过这种情况:用户填写了一份复杂的表格,然后网络出现故障,WebSocket 连接断开,Blazor 服务器重新连接。

他们的所有数据?都没了。他们得重新开始。他们会放弃你的应用。他们会写差评。

.NET 10 修复了这个问题:

// When the user goes idle or connection is unstable

Blazor.pause();

// Later, when they're back

await Blazor.resume();

在连接断开之前调用时Blazor.pause(),.NET 10 会将电路状态持久化到浏览器存储中。即使服务器上的原始电路已被清除,您也可以稍后恢复连接。实际上,这种持久性取决于您的服务器设置和浏览器的存储生命周期。请将其理解为"更具弹性",而不是"无限会话永存"。

这使得 Blazor Server 在高要求的企业级应用场景中更具弹性。更多详情,请参阅 Microsoft 的Blazor Server 重新连接文档。

性能:Blazor .NET 10 基准测试

以下是一个典型的 Blazor Web 应用的修改前后对比图(具体数值会根据配置和应用结构而有所不同):

资产 .NET 9 .NET 10 改进
blazor.web.js 约183 KB 约43 KB 缩小约 76%
启动清单 单独的文件 内联dotnet.js 请求减少 1 次
资产交付 嵌入式资源 静态指纹识别 更好的缓存

那个 JavaScript 包?它已经大幅精简了。你的 Lighthouse 分数会感谢你的。

等等,还有更多惊喜。.NET 10 会在 Blazor Web 应用中通过 Link 标头自动预加载 Blazor 框架资源,并在独立 WebAssembly 中启用高优先级下载。运行时会页面渲染时下载资源,而不是渲染后再下载。

首次渲染完成,用户可以看到内容。与此同时,Blazor 运行时在后台下载。无需任何特殊组件------框架会自动处理。

这就像"这个应用运行缓慢"和"这个应用运行瞬间完成"之间的区别。

Blazor .NET 10 中的 JavaScript 互操作

旧版 JS 的互操作性是......函数式的。你可以调用函数,可以传递基本类型,但你只能祈祷 JSON 序列化不会崩溃。

.NET 10 增加了实际的对象语义:

// Create a JS object instance

var chart = await JS.InvokeConstructorAsync<IJSObjectReference>(

"Chart",

canvasElement,

chartConfig

);

// Read properties

var currentValue = await chart.GetValueAsync<int>("currentValue");

// Set properties

await chart.SetValueAsync("animationDuration", 500);

// For performance-critical code (in-process only)

var syncValue = chart.GetValue<int>("dataLength");

现在你操作的是实际对象,不再只是字符串和祈祷文了。对于嵌套很深的属性路径,通常仍然需要编写一个简单的 JavaScript 辅助函数,并从 .NET 中调用它。完整的 API 参考请参见"从 .NET 调用 JavaScript" 。

真正的胜利:制造商的支持

以前,创建JS对象看起来像这样:

await JS.InvokeVoidAsync("eval", "window.myChart = new Chart(canvas, config)");

var reference = await JS.InvokeAsync<IJSObjectReference>("eval", "window.myChart");

评估。在生产代码中。我见过。我也写过。我们都见过。

现在:

var myChart = await JS.InvokeConstructorAsync<IJSObjectReference>("Chart", canvas, config);

无需评估,不会造成全球污染,只有纯净的互操作性。

WebAssembly热重载

Blazor 的热重载功能一直褒贬不一。服务器端表现相当不错。WebAssembly 端呢?需要手动配置,而且有时根本就没法配置。

.NET 10 迁移到了 WebAssembly 的通用热重载实现。SDK 现在会自动处理它。

<!-- This is now true by default for Debug builds -->

<PropertyGroup>

<WasmEnableHotReload>true</WasmEnableHotReload>

</PropertyGroup>

您无需添加此功能,它已启用。编辑.razor文件并保存,即可立即查看更改。无需重新构建,无需刷新浏览器,也无需任何配置。

对于具有自定义构建配置的团队:

<!-- Enable for non-Debug configurations -->

<PropertyGroup Condition="'$(Configuration)' == 'Staging'">

<WasmEnableHotReload>true</WasmEnableHotReload>

</PropertyGroup>

工作流程终于达到了应有的水平:编辑代码、保存、查看更改。Blazor WASM 的内部开发循环现在几乎可以与 JavaScript 框架多年来一直拥有的流程相媲美。

表单验证源生成器

这一点虽然细微,但却很重要。

**.NET 10 之前:**验证使用反射。每次表单提交时,运行时都会遍历模型,查找属性并构建验证器。虽然可行,但对 AOT 不友好,而且速度也不快。

.NET 10 之后:

// Program.cs

builder.Services.AddValidation();

// Your model

ValidatableType // New: enables source-generated validators in .NET 10

public class OrderForm

{

Required

public string CustomerName { get; set; }

Range(1, 100)

public int Quantity { get; set; }

ValidateComplexType // Now works with the source generator for nested objects

public Address ShippingAddress { get; set; }

ValidateEnumeratedItems // Collection items validated via the generator

public List<LineItem> Items { get; set; }

}

源代码生成器在编译时运行,它会创建优化的验证器,无需运行时反射,兼容 AOT,速度更快。有关实现细节,请参阅Blazor 表单和验证。

最后------终于 ------嵌套对象验证功能真正实现了。无需再使用任何自定义ValidationAttribute技巧。

404 处理方式合情合理

我们之前是如何处理 404 错误的?

@page "/products/{id}"

@if (_notFound)

{

<NotFoundPage />

}

else if (_loading)

{

<Loading />

}

else

{

<ProductDetails Product="_product" />

}

@code {

private bool _loading = true;

private bool _notFound = false;

private Product? _product;

protected override async Task OnInitializedAsync()

{

_product = await ProductService.GetById(Id);

_notFound = _product == null;

_loading = false;

}

}

每个组件。每个页面。手动跟踪"这个东西是否存在"。

.NET 10:

@page "/products/{id}"

@inject NavigationManager Nav

@code {

private Product? _product;

protected override async Task OnInitializedAsync()

{

_product = await ProductService.GetById(Id);

if (_product is null)

{

Nav.NotFound(); // That's it. That's the whole thing.

return;

}

}

}

支持服务器端渲染 (SSR)。支持交互模式。支持所有平台。框架会处理 404 响应、页面显示等所有操作。

新模板甚至包含一个NotFound.razor开箱即用的组件。只需自定义一次,即可在任何地方使用。

导航改进

这件事困扰了我好几年。

用户正在阅读一个很长的页面。他们点击了一个过滤器,更新了查询字符串。Blazor 跳转到了"新"的 URL。页面滚动到顶部。用户找不到刚才阅读的位置。用户咒骂你的名字。

**已在 .NET 10 中修复。**同页面导航不再滚动到顶部。查询字符串更改会保留视口。片段更改也能正常工作。

所以:

<NavLink href="/products?category=electronics" Match="NavLinkMatch.All">

Electronics

</NavLink>

启用此功能后NavLinkMatch.All,即使您&sort=price在 URL 中添加了参数,它仍然有效。查询字符串和片段在匹配时将被忽略。

细节决定成败。

重新连接界面更新

Blazor Server 中的默认重新连接模态框一直看起来......还不错。很普通。就像是 2019 年由一群人共同设计的(因为它确实是)。

.NET 10 模板包含一个新ReconnectModal组件:

Components/

├── ReconnectModal.razor

├── ReconnectModal.razor.css # Collocated styles

└── ReconnectModal.razor.js # Collocated scripts

它符合CSP标准。它可自定义。它看起来就像是你的应用的一部分。

而且还有一个新活动可以参与:

document.addEventListener('components-reconnect-state-changed', (e) => {

console.log(`Reconnection state: ${e.detail.state}`);

// States: 'connecting', 'connected', 'failed', 'retrying' (new!)

});

状态retrying已更新。现在您可以显示"正在尝试重新连接,这是第 3 次尝试,共 10 次......"而不是一直显示加载中。

支持密码认证

ASP.NET Core Identity 现在支持 WebAuthn/FIDO2。简而言之,就是指纹和人脸登录、硬件安全密钥以及防钓鱼身份验证。

// Program.cs

builder.Services.AddIdentityCore<ApplicationUser>(options =>

{

options.SignIn.RequireConfirmedAccount = true;

options.Stores.SchemaVersion = IdentitySchemaVersions.Version3; // Enables passkeys

})

.AddEntityFrameworkStores<ApplicationDbContext>()

.AddSignInManager()

.AddDefaultTokenProviders();

Blazor Web App 模板搭建了密码端点和 UI------大部分工作是配置,而不是手动编写 WebAuthn:

  • 注册通行密钥
  • 列出已注册的通行密钥
  • 移除密码
  • 使用密码登录
  • 无密码账户创建(是的,完全支持无密码流程)

无需第三方库,无需复杂的 WebAuthn 实现,它就能正常工作。有关实现指南,请参阅ASP.NET Core 身份验证简介

QuickGrid 更新

如果您正在使用QuickGrid(强烈推荐使用,它非常出色),以下两项改进将提升您的使用体验:

<QuickGrid Items="_orders" RowClass="@GetRowClass">

<PropertyColumn Property="@(o => o.Id)" />

<PropertyColumn Property="@(o => o.Status)" />

</QuickGrid>

@code {

private string GetRowClass(Order order) => order.Status switch

{

OrderStatus.Overdue => "row-danger",

OrderStatus.Pending => "row-warning",

_ => ""

};

}

基于数据的动态行样式。终于实现了。

列选项如下:

private async Task ApplyFilter()

{

// Apply your filter logic

await _grid.HideColumnOptionsAsync(); // Close the popup programmatically

}

小事,大效率提升。

WebAssembly改进

改进的服务验证

你知道什么最有趣吗?注册一个服务时设置了错误的生命周期。然后在运行时发现它。在生产环境中。

// Misconfigured lifetimes are now caught with clear diagnostics

builder.Services.AddScoped<MySingletonDependency>(); // Oops, should be Singleton

builder.Services.AddSingleton<MyService>(); // Depends on scoped service

在 .NET 10 WebAssembly 中,配置错误的生命周期(例如依赖于作用域服务的单例)会被清晰地诊断出来,而不会在运行时崩溃。这是由构建管道捕获的,而不是用户捕获的。

默认响应流

var response = await Http.GetAsync("/api/large-dataset");

var stream = await response.Content.ReadAsStreamAsync();

// In .NET 10, this uses BrowserHttpReadStream automatically

// Memory efficient. No massive allocations.

大文件下载不再占用所有内存。浏览器的流媒体功能得到了充分利用。

性能诊断

WASM 的新型诊断工具:

  • CPU性能概况
  • 内存转储
  • 性能计数器
  • 原生 WebAssembly 指标

最后,您可以找出应用运行缓慢的原因 ,而不仅仅是知道它运行缓慢。有关 Blazor WebAssembly 的更多信息,请参阅ASP.NET Core Blazor WebAssembly

迁移清单

要从 .NET 9 升级?请检查以下事项:

  • 更新 TFM 至net10.0
  • builder.Services.AddValidation()如果使用新的验证方式,请添加
  • 查看重新连接界面------新组件已可用,但未自动应用
  • 测试导航行为------同页面导航不再滚动
  • 考虑[PersistentState]替换手动状态持久化
  • 查看 JS 互操作性------新增 API,实现更简洁的对象处理
  • 如果使用身份认证,请测试密码密钥支持

避免常见错误

1. 过度使用[PersistentState]

// Don't do this

PersistentState

public DateTime LastClick { get; set; } // Why are you persisting this?

PersistentState

public bool IsMenuOpen { get; set; } // UI state doesn't need persistence

持久化数据,而非用户界面状态。序列化开销并非没有。

2. 忘记新的验证注册

// This won't work with ValidatableType

builder.Services.AddRazorComponents();

// You need this too

builder.Services.AddValidation();

源生成器创建验证器AddValidation()并注册它们。

3. 假设通行密钥在所有地方都有效

// Check for support first

if (await PasskeyService.IsSupported())

{

// Show passkey options

}

else

{

// Fallback to password

}

老款 iOS 系统上的 Safari 浏览器?某些安卓浏览器?策略奇葩的企业网络?密码支持情况不一。务必准备备用方案。

4. 忽略电路暂停/恢复

// The connection died, but you didn't pause first

// State is lost. User is sad.

// Do this instead

window.addEventListener('beforeunload', () => {

Blazor.pause();

});

只有当你明确告诉 Blazor 持久化电路状态时,电路状态持久化才会生效。

何时升级

情况 推荐
新项目 当然要从.NET 10开始。
.NET 9,太棒了! 如果您对升级比较谨慎,可以考虑等待第一个 .NET 10 服务更新。
.NET 9 遇到状态问题 现在就升级吧,[PersistentState]光这一点就值回票价。
.NET 8 LTS 你可以在2026年11月之前完成迁移。但第10年也是长期支持计划(LTS)。做好迁移计划吧。
.NET 6 或更早版本 你在干什么?昨天就升级了。

.NET 10 是一个长期支持 (LTS) 版本,提供三年支持。它是生产应用程序的理想选择。

常见问题解答

.NET 10 是 LTS 版本吗?

是的,.NET 10 是一个长期支持 (LTS) 版本,微软提供三年的支持。这使其成为需要稳定性和长期维护的生产应用程序的理想选择。之前的 LTS 版本是 .NET 6 和 .NET 8。

Blazor .NET 10 的最大改进是什么?

可以说,这个[PersistentState]属性的加入是影响最大的改动。它将 25 行以上的手动状态序列化代码简化为一个属性,从而消除了预渲染和水合期间的重复渲染问题和冗余 API 调用。

Blazor .NET 10 是否支持密码?

是的,ASP.NET Core Identity 现在内置了对 WebAuthn/FIDO2 密钥身份验证的支持。这使得指纹登录、面容 ID 和硬件安全密钥无需第三方库即可使用。Blazor Web 应用模板会自动生成必要的端点和 UI 组件。

.NET 10 中的 Blazor JavaScript 包体积缩小了多少?

.NET 10 中,该blazor.web.js软件包的大小从 .NET 9 的约 183 KB 减少到约 43 KB,体积缩小了约 76%。此外,启动清单现在已内联,从而在启动过程中减少了一次额外的 HTTP 请求。

我可以在 .NET 10 中使用 Blazor WebAssembly 的热重载功能吗?

是的,Blazor WebAssembly Debug 版本现在默认启用热重载。编辑文件.razor并保存后,无需重新构建或刷新浏览器即可立即看到更改。对于非 Debug 配置(例如 Staging),您可以使用以下命令手动启用它<WasmEnableHotReload>true</WasmEnableHotReload>

如何从 .NET 9 迁移到 .NET 10?

更新目标框架名称 (TFM) net10.0builder.Services.AddValidation()如果使用新的源生成验证器,请添加相关配置,查看新的重新连接 UI 组件,并测试导航行为的更改。考虑使用[PersistentState]属性替换手动状态持久化。有关详细信息,请参阅迁移清单部分。

最后想说的话

每次 Blazor 版本发布,我都会寻找能够让开发体验更流畅、减少缺陷的改进之处。

.NET 10 表现出色。

Blazor 已经成熟,可以投入生产环境多年------许多企业自 2019 年以来就一直在成功运行它。但 .NET 10 更进一步。状态管理不再仅仅是函数式的,而是更加优雅。性能卓越。JavaScript 互操作性也更加现代化。开发者体验显著提升。

它完美吗?不。调试机制仍需改进。组件库生态系统仍然比 React 小。WASM 中的一些极端情况仍然需要变通方法。

但 .NET 10 消除了我过去在推荐 Blazor 时提到的许多注意事项。它不仅是 .NET 团队的可靠选择,而且完全可以与任何现代 Web 框架相媲美。

.NET 10 提高了标准。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

相关推荐
Niyy_5 分钟前
WASM 的使用笔记
jvm·笔记·wasm
夜雪闻竹6 小时前
sql.js WASM 深度解析
javascript·sql·wasm
小杍随笔4 天前
【Rust 1.96.0 深度解析:让 Range 可 Copy、让断言更聪明、让 Wasm 更安全】
安全·rust·wasm
W清风大侠M5 天前
基于 Blazor 实现的电梯运行监测系统
blazor
known6 天前
基于 Blazor 实现的电梯运行监测系统
blazor·known
padane226 天前
gmssl编译wasm
ubuntu·html·密码学·wasm·js
放逐者-保持本心,方可放逐6 天前
Go + WebAssembly 构建树木数据统计分析系统
开发语言·golang·wasm·javascipt
functionMC7 天前
在Vue/Nuxt、React/Next/TanstackStart、RazorPages折腾一圈后,还是回到了Blazor,但这回有SSR+HTMX+Alpine的加持
blazor·alpine·htmx
包子源7 天前
浏览器 ffmpeg.wasm 视频压缩:Next.js 静态站集成完整指南
javascript·ffmpeg·wasm
夜雪闻竹12 天前
sql.js WASM 实战:浏览器里跑 SQLite
javascript·sql·wasm