.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\ Items { get; set; } } 源代码生成器在编译时运行,它会创建优化的验证器,无需运行时反射,兼容 AOT,速度更快。有关实现细节,请参阅[Blazor 表单和验证。](https://learn.microsoft.com/en-us/aspnet/core/blazor/forms "Blazor 表单和验证。") 最后------*终于* ------嵌套对象验证功能真正实现了。无需再使用任何自定义`ValidationAttribute`技巧。 ### **404 处理方式合情合理** 我们之前是如何处理 404 错误的? @page "/products/{id}" @if (_notFound) { \ } else if (_loading) { \ } else { \ } @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`开箱即用的组件。只需自定义一次,即可在任何地方使用。 ![](https://i-blog.csdnimg.cn/direct/a008124775e74b5ca4a8422fa1cd2fa3.png) ### **导航改进** 这件事困扰了我好几年。 用户正在阅读一个很长的页面。他们点击了一个过滤器,更新了查询字符串。Blazor 跳转到了"新"的 URL。页面滚动到顶部。用户找不到刚才阅读的位置。用户咒骂你的名字。 **已在 .NET 10 中修复。**同页面导航不再滚动到顶部。查询字符串更改会保留视口。片段更改也能正常工作。 所以: \ Electronics \ 启用此功能后`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 次......"而不是一直显示加载中。 ![](https://i-blog.csdnimg.cn/direct/f503634d671341f4abecb6ce700db688.png) ### **支持密码认证** ASP.NET Core Identity 现在支持 WebAuthn/FIDO2。简而言之,就是指纹和人脸登录、硬件安全密钥以及防钓鱼身份验证。 // Program.cs builder.Services.AddIdentityCore\(options =\> { options.SignIn.RequireConfirmedAccount = true; options.Stores.SchemaVersion = IdentitySchemaVersions.Version3; // Enables passkeys }) .AddEntityFrameworkStores\() .AddSignInManager() .AddDefaultTokenProviders(); Blazor Web App 模板搭建了密码端点和 UI------大部分工作是配置,而不是手动编写 WebAuthn: * 注册通行密钥 * 列出已注册的通行密钥 * 移除密码 * 使用密码登录 * 无密码账户创建(是的,完全支持无密码流程) 无需第三方库,无需复杂的 WebAuthn 实现,它就能正常工作。有关实现指南,请参阅[ASP.NET Core 身份验证简介](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity "ASP.NET Core 身份验证简介")。 ### **QuickGrid 更新** 如果您正在使用[QuickGrid](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/quickgrid "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 } 小事,大效率提升。 ![](https://i-blog.csdnimg.cn/direct/2666accd675d4fdca3b7601ed38e6fe8.png) ### **WebAssembly改进** #### **改进的服务验证** 你知道什么最有趣吗?注册一个服务时设置了错误的生命周期。然后在运行时发现它。在生产环境中。 // Misconfigured lifetimes are now caught with clear diagnostics builder.Services.AddScoped\(); // Oops, should be Singleton builder.Services.AddSingleton\(); // 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](https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models#blazor-webassembly "ASP.NET Core Blazor WebAssembly")。 ![](https://i-blog.csdnimg.cn/direct/f8d81aacf2bd4a599aeceeb298b5de4c.png) ### **迁移清单** 要从 .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 提高了标准。

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

相关推荐
狗都不学爬虫_8 小时前
JS逆向 - Akamai阿迪达斯(三次) 补环境、纯算
javascript·爬虫·python·网络爬虫·wasm
小陈工1 天前
2026年3月30日技术资讯洞察:AI算力突破、云原生优化与架构理性回归
开发语言·人工智能·python·云原生·架构·数据挖掘·wasm
小陈工5 天前
2026年3月26日技术资讯洞察:WebAssembly崛起、AI代码质量危机与开源安全新挑战
人工智能·python·安全·架构·开源·fastapi·wasm
七夜zippoe8 天前
WebAssembly与Python:在浏览器中运行Python
开发语言·python·wasm·webassembly·pyscript
爱学习的程序媛8 天前
【Web前端】WebAssembly详解
前端·web·wasm
林鸿群8 天前
.NET 逆向工程实战:将 Game.Utils 和 Game.Kernel 从 .NET 2.0 升级到 .NET 10
逆向·.net10·.net2.0
爱学习的程序媛10 天前
【Web前端】WebAssembly实战项目
前端·web·wasm
REDcker11 天前
Wasm 软解 H.265 方案与原理
wasm·h.265
步步为营DotNet17 天前
ASP.NET Core 10中的Blazor WebAssembly性能优化实践
性能优化·asp.net·wasm
前端之虎陈随易18 天前
Vite 8正式发布,内置devtool,Wasm SSR 支持
前端·人工智能·typescript·npm·node.js·wasm