news 2026/9/9 21:07:46

深入 ASP.NET Core 源码中的 Blazor Components 模块:共享组件模型、源码构建与 E2E 测试实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入 ASP.NET Core 源码中的 Blazor Components 模块:共享组件模型、源码构建与 E2E 测试实践

深入 ASP.NET Core 源码中的 Blazor Components 模块:共享组件模型、源码构建与 E2E 测试实践

【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore

导读

本指南基于 aspnetcore 仓库 src/Components/README.md 展开,系统梳理 Blazor 在 ASP.NET Core 中共享组件模型(component model)的源码组织方式。无论你的 Blazor 应用运行于 WebAssembly(浏览器端)还是 Server(SignalR 交互)托管模型,其组件基础都源自src/Components这一个目录。读完本文,你将掌握该模块的目录结构与各子项目职责、如何在本仓库中从源码构建 Components、如何使用 XUnit 与 Selenium 运行其单元/端到端测试,以及 WebAssembly 裁剪(trimming)对测试的潜在影响,并理解组件模型背后的核心类型(IComponentComponentBaseRenderFragment等)在源码中的落点。

一、定位:一个组件模型支撑两种托管模型

Blazor 是一个用 C# 与 Razor 构建现代化、交互式 Web UI 的框架。它在浏览器与服务器两侧的形态差异巨大:

  • Blazor WebAssembly:组件在浏览器内以 .NET(WASM)运行,使用 JavaScript interop 与 DOM 交互;
  • Blazor Server:组件在服务器端运行,通过 SignalR 实时连接把 UI 更新推送到浏览器;
  • Blazor Hybrid(WebView):在原生客户端应用中用 WebView 承载组件(对应本目录中的WebView)。

尽管托管模型不同,二者却共享同一套组件模型(component model)——生命周期、渲染、参数绑定、事件回调、级联参数、路由与 Forms 等都是同一套实现。这正是src/Components目录存在的意义:正如该 README 所述,"This folder contains the component model shared between the WebAssembly and Server hosting models for Blazor"(本目录包含 Blazor WebAssembly 与 Server 两种托管模型共享的组件模型)。

在仓库中,这一共享模型的实现主体位于 src/Components/Components,核心程序集为 Microsoft.AspNetCore.Components.csproj,其中:

  • IComponent.cs 定义了所有组件必须实现的最小契约(渲染句柄RenderHandle的接收与参数设置);
  • ComponentBase.cs 是基于IComponent的默认基类,绝大多数.razor组件编译后都继承它;
  • RenderFragment.cs 是描述"一段可渲染 UI"的委托,ComponentBase的渲染内容本质就是一组RenderFragment的组合;
  • 目录下还有 EventCallback.cs(事件回调)、ParameterView.cs(参数集合)、CascadingValue.cs(级联参数)、NavigationManager.cs(导航抽象)、RouteView.cs(路由视图)、DynamicComponent.cs、ErrorBoundaryBase.cs 等核心抽象,以及RenderTree/Rendering/Routing/CompilerServices/等子目录承载渲染树、调度与编译服务细节。

二、目录结构深度导览

README 对该目录下的每个子目录做了精确定义。下面按原文骨架逐项展开,并标注其在本仓库中的对应位置与要点:

README 所列子目录职责描述仓库中的代表内容
Analyzers面向 Razor 组件的 Roslyn 分析器集合src/Components/Analyzers,内含 50+ 个.cs分析器源文件与两组 csproj,用于在编译期对组件代码做诊断与代码修复
AuthorizationBlazor 中与认证/授权相关的组件与服务src/Components/Authorization,提供AuthorizeViewAuthorizeRouteViewCascadingAuthenticationState等授权组件的实现
ComponentsBlazor 组件模型的核心实现src/Components/Components,即上一节所述的IComponent/ComponentBase/渲染树等公共实现,以及perf/基准与test/单测
FormsBlazor 表单组件源码src/Components/Forms,对应EditForm与输入组件的数据校验/绑定基础
SamplesBlazor 示例应用集合src/Components/Samples,提供可直接运行的 demo 工程
ServerBlazor Server 特有组件的实现src/Components/Server,含src/test/,是 Circuit(SignalR 会话)与ComponentHub等 Server 侧机制的家
Shared共享常量与辅助方法/类的集合src/Components/Shared,供本模块内部多个项目复用
WebDOM 事件、表单及相关组件的浏览器侧源码src/Components/Web,对应Microsoft.AspNetCore.Components.Web程序集(WebEventCallbackHtmlRenderer扩展等面向浏览器的部分)
Web.JSBlazor 客户端 JavaScript 源文件src/Components/Web.JS,编译产物随 Web 程序集一起分发
WebAssemblyWebAssembly 托管模型特有实现src/Components/WebAssembly,内部再分多个子项目(详见下文)
WebView支撑dotnet/mauiBlazor Hybrid的源文件src/Components/WebView,配合 MAUI 的 BlazorWebView 使用

README 对WebAssembly目录又做了细分,本仓库快照中可确认到以下同级子目录:Authentication.Msal(WASM 应用中 MSAL 认证实现)、DevServer(Blazor 开发服务器)、JSInterop(从 .NET 调用 JS 的方法实现,即DotNetObjectReference/JSInProcessRuntime等的载体)、Server(WASM 特有扩展方法及调试代理启动逻辑)、WebAssembly(渲染器、HostBuilder 等 WASM 具体实现,见 src/Components/WebAssembly/WebAssembly)、WebAssembly.Authentication(WASM 侧认证具体实现),另有testassets/存放测试资产。

值得说明:README 的目录清单反映的是该模块的核心稳定结构。在当前仓库快照中,src/Components下还演进出了若干新目录,如 AI、Endpoints(Razor 组件接入 HTTP 端点/流式渲染)、Gateway、Media、QuickGrid、Server.AutoPause、Testing、CustomElements 以及benchmarkapps/dotnet-runtime-js/等。阅读时可将其视为对上述骨架的持续扩展。

2.1 从目录层级理解"共享"

WebServerWebAssembly三者位于同一层级,都是"平台侧"实现;而Components(核心组件模型)、FormsAuthorization等则属于"模型层"的共享能力。这样组织的好处是:当你新写一个.razor组件时,面向的 API 一律来自共享模型层;运行在哪个托管模型下,只是编译/打包时选择挂接WebServer还是WebAssembly平台程序集的问题。

三、从源码构建 Components 模块

README 明确提示:若要从源码构建其他具体项目,应先参考 从源码构建的文档(其"Step 3: Build the repo"小节)。在此基础上,构建 Components 模块按以下步骤进行。

3.1 前置条件与仓库清理

  1. 安装 Node.js:Components 的 JS 资产需要 npm 处理。

  2. 清理仓库:强烈建议在切换分支或更新工作分支后,先移除上一次构建残留的任何资产:

    git clean -xdff

    若文件被占用导致删除失败,可能需要先关闭 Visual Studio 及残留的msbuild/dotnet进程。README 同时提醒:可能还有无头(headless)chrome进程存在,但它们不在此命令覆盖范围内。可执行下面的命令(注意:它也会终止其他重要任务,需谨慎使用):

    Get-Process dotnet, escape-node-job, msbuild, VBCSCompiler, node, vstest.console, Microsoft.CodeAnalysis.LanguageServer -ErrorAction Continue | Stop-Process

3.2 还原依赖与生成资产

  1. 还原 JS 模块:使用 npm 的离线模式还原,无需外网——依赖源来自仓库的子模块(sub-module):

    npm ci --offline
  2. 还原 .NET 依赖并初始化仓库:执行仓库根目录下的还原脚本(Linux/macOS 用./restore.sh,Windows 用./restore.cmd):

    # Linux 或 Mac ./restore.sh
    # Windows ./restore.cmd
  3. 构建仓库所需的全部 JS 资产(如 SignalR 等),运行:

    npm run build
  4. 构建 Components 本体:README 给出的命令为./src/Components/build.cmd;构建产物完成后,可选用 Visual Studio 打开组件工程:仓库根目录提供了Components.slnfComponentsNoDeps.slnf两个解决方案筛选文件,同时可用 startvs.cmd(Windows/VS 场景)或startvscode.cmd/startvscode.sh一键唤起 IDE。

提示:本仓库的快照根目录同时提供 restore.cmd / restore.sh 与 activate.ps1 / activate.sh,前者负责还原,后者用于在构建/测试前激活本地安装的 .NET 环境。若你只改动了某个具体项目而非整个模块,建议按 docs/BuildFromSource.md 的指引仅构建受影响的项目,以节省时间。

四、测试体系:XUnit 单元测试 + Selenium E2E 测试

Components 模块的测试分为两层:

  • 单元测试:基于 XUnit 实现,与各src平级的test/目录(如 src/Components/Components/test)中。
  • 端到端(E2E)测试:基于 Selenium 实现,需要本机装有Node v16+。E2E 测试工程位于 src/Components/test/E2ETest(README 中写作tests/E2ETest,在当前仓库布局中其实际路径为上述位置),内含Tests/(各功能场景)、ServerExecutionTests/ServerRenderingTests/以及Infrastructure/等目录。

4.1 E2E 测试架构:TestServer × BasicTestApp

README 描述了一个巧妙的"一测多宿主"架构,与仓库实际结构一一对应:

  • 测试资产目录src/Components/test/testassets 下有一个顶层TestServer(即 Components.TestServer),它针对特定场景实例化不同的应用服务器:

    • Standalone(独立)Blazor WebAssembly;
    • Hosted(承载式)Blazor WebAssembly;
    • Blazor Server;
  • Blazor Server with pre-rendering(预渲染)。

    从该工程目录可以直观印证这一点:Program.cs与多份*Startup.cs(如 ServerStartup.cs、ClientStartup.cs、PrerenderedStartup.cs)分别对应不同宿主场景的配置入口,而 MultipleComponents.cs 等则服务于特定的组件挂载测试。

  • 每个应用服务器都把同一个BasicTestApp应用挂载到各场景之下BasicTestApp位于 src/Components/test/testassets/BasicTestApp,其中以组件为单位覆盖了极广的功能面,例如:

    • 生命周期/渲染类:ParentChildComponent.razorAsyncDisposableComponent.razorConcurrentRenderParent.razorVirtualizationComponent.razor
    • 事件/绑定类:EventBubblingComponent.razorBindCasesComponent.razorTouchEventComponent.razorMouseEventComponent.razor
    • 互操作类:InteropComponent.razorDotNetToJSInterop.razorJavaScriptRootComponents.razor
    • 路由/导航/状态类:RouterTest/PreserveStateComponent.razorPrerenderedToInteractiveTransition.razor
    • 表单/级联/模板类:FormsTest/CascadingValueTest/TemplatedTable.razor等。

    其目录名几乎覆盖了组件模型的全部公开能力——这正是用"同一份被测应用 × 多种宿主"验证"模型是共享的"这一核心命题的实证。

在 CI 中,这些 E2E 测试作为aspnetcore-components-e2e管道的一部分运行。

4.2 命令行运行 E2E 测试

完成第三节的构建步骤后,按如下方式执行:

  1. 激活本地安装的 .NET

    # Linux 或 Mac source activate.sh
    # Windows . ./activate.ps1
  2. 启动测试

    dotnet test ./src/Components/test/E2ETest
  3. 按名称过滤:可用--filter参数只跑关心的用例,例如:

    dotnet test --filter <TEST_NAME> ./src/Components/test/E2ETest

E2E 运行参数(如浏览器类型、基地址等)可参考 E2E 工程根目录下的 e2eTestSettings.json(及其 CI 变体e2eTestSettings.ci.json)。若需要更完整的从源码构建与测试指引,请查阅 docs/BuildFromSource.md。

4.3 WebAssembly 裁剪(Trimming)带来的测试差异

README 特别提醒了一个"本地通过、CI 失败"的高频陷阱:默认情况下,作为 CI 一部分运行的、或以 Release 配置运行的 WebAssembly E2E 测试都开启了裁剪(trimming)。裁剪会把 IL 中未被使用的代码移除,因此可能把本地正常运行的用例"剪坏",导致 CI 失败。

要在本地复现该场景,有两种方式:

# 方式一:以 Release 配置运行(Release 构建默认开启裁剪) dotnet test -c Release
# 方式二:显式设置 TestTrimmedApps 属性后先构建、再跑 dotnet build /p:TestTrimmedApps=true dotnet test --no-build

其中方式二先把带TestTrimmedApps=true的应用构建出来,再用--no-build跳过重复构建直接测试,可显著缩短迭代周期。从 BasicTestApp 等测试资产工程与 E2E 基础设施的配置看,裁剪相关的属性正是通过这类 MSBuild 属性在各测试宿主间传递的。因此,当你的 E2E 用例在本地(默认 Debug)通过却在 CI(Release + 裁剪)失败时,应优先按上述方式在本地复现,定位是否因裁剪引入了对反射/动态代码的依赖。

五、从 README 到源码的验证路径

src/Components/README.md的最后一节将读者导向仓库根 README.md。对希望深入源码的读者,这里给出几条从文档描述直通实现的检索路径:

  • 组件模型核心:阅读 IComponent.cs → ComponentBase.cs → RenderFragment.cs,即可理解"组件 = 接收RenderHandle+ 在参数变化时重建渲染树"的最小模型;
  • 渲染与调度RenderTree/Rendering/子目录存放渲染树构建、批处理(batch)与调度器(dispatcher)实现,是理解"共享模型如何同时服务于 Server 与 WASM"的关键;
  • 参数与事件ParameterView/EventCallback系列类型(ParameterView.cs、EventCallback.cs)定义了父子组件之间数据与回调的传递契约;
  • E2E 行为验证:在 BasicTestApp 中搜索对应组件名(如VirtualizationComponent.razorDynamicComponentRendering.razor),再在 E2ETest/Tests 中查找同名测试类,即可看到"组件能力描述 ↔ 测试资产 ↔ 端到端断言"的完整闭环。

六、小结

src/Components是 aspnetcore 仓库中 Blazor 技术的"总装车间":以 Components 为核心实现共享组件模型,以 Web/Server/WebAssembly/WebView 为各托管模型提供平台侧支持,再以 Analyzers、Forms、Authorization、QuickGrid 等子模块向开发者交付开箱即用的功能。配合"TestServer × BasicTestApp"的多宿主 E2E 测试体系,仓库用同一套用例反复验证了"组件模型跨托管模型共享"这一设计承诺。无论你是想给 Blazor 提交修复、探索组件渲染原理,还是复现与 WebAssembly 裁剪相关的疑难测试问题,本文梳理的目录地图与构建/测试命令都能成为你的起点。

【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 21:05:55

Kafka事务消息脏读事故:read_committed与read_uncommitted隔离级别解析

1. 事故现场&#xff1a;消息队列里凭空多出来的“鬼消息” 1.1 现象&#xff1a;日志一切正常&#xff0c;数据却对不上账 复盘这事之前&#xff0c;先描述一下事故当天的情况。我们有个订单状态同步链路&#xff1a;A系统负责接收支付回调&#xff0c;把订单状态发到Kafka&a…

作者头像 李华
网站建设 2026/9/9 21:04:32

Windows与Ubuntu双机共享键鼠实战:Barrier配置与踩坑指南

最近收拾办公桌&#xff0c;发现桌面已经被两套键鼠占满了&#xff0c;一套接 Windows 主力机&#xff0c;一套接 Ubuntu 开发机。每天在两个屏幕之间来回伸手够键盘、够鼠标&#xff0c;说实话挺折腾的。后来想明白一件事&#xff1a;如果能让两台电脑共用一套键鼠&#xff0c…

作者头像 李华
网站建设 2026/9/9 21:04:10

STM32F103 SPI驱动AD7124高精度ADC实战:寄存器配置与数据采集

简介&#xff1a;面向STM32F103开发者提供的AD7124驱动完整工程&#xff0c;适用于工业测量、仪器仪表及多通道传感器信号采集等需要高精度ADC的场景&#xff0c;解决了芯片初始化、6通道双极性采样与外部参考电压配置等关键问题。工程基于Keil5构建&#xff0c;采用模拟SPI实现…

作者头像 李华
网站建设 2026/9/9 21:04:07

AD9361 Vivado例程实战:工程生成、数据通路与踩坑复盘

简介&#xff1a;AD9361 Vivado例程是一套面向FPGA与软件定义无线电开发者的完整工程参考包&#xff0c;基于Xilinx Vivado 2016.4环境&#xff0c;展示如何通过IP核集成、AXI接口互联、参数配置与时序约束驱动AD9361射频收发器&#xff0c;适用于无线通信、测试测量等场景的开…

作者头像 李华
网站建设 2026/9/9 21:04:00

航天级AI Agent工程实践:200+轻量自治单元的实时协同架构

1. 这不是科幻片&#xff0c;是SpaceX工程师日常写的AI Coding工作流你可能在技术社区刷到过那张流传甚广的截图&#xff1a;一个终端窗口里密密麻麻滚动着200个正在运行的进程标签&#xff0c;每个标签都以agent_开头&#xff0c;后面跟着任务编号、模块名和状态码&#xff1b…

作者头像 李华