深入 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)对测试的潜在影响,并理解组件模型背后的核心类型(IComponent、ComponentBase、RenderFragment等)在源码中的落点。
一、定位:一个组件模型支撑两种托管模型
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,用于在编译期对组件代码做诊断与代码修复 |
Authorization | Blazor 中与认证/授权相关的组件与服务 | src/Components/Authorization,提供AuthorizeView、AuthorizeRouteView、CascadingAuthenticationState等授权组件的实现 |
Components | Blazor 组件模型的核心实现 | src/Components/Components,即上一节所述的IComponent/ComponentBase/渲染树等公共实现,以及perf/基准与test/单测 |
Forms | Blazor 表单组件源码 | src/Components/Forms,对应EditForm与输入组件的数据校验/绑定基础 |
Samples | Blazor 示例应用集合 | src/Components/Samples,提供可直接运行的 demo 工程 |
Server | Blazor Server 特有组件的实现 | src/Components/Server,含src/与test/,是 Circuit(SignalR 会话)与ComponentHub等 Server 侧机制的家 |
Shared | 共享常量与辅助方法/类的集合 | src/Components/Shared,供本模块内部多个项目复用 |
Web | DOM 事件、表单及相关组件的浏览器侧源码 | src/Components/Web,对应Microsoft.AspNetCore.Components.Web程序集(WebEventCallback、HtmlRenderer扩展等面向浏览器的部分) |
Web.JS | Blazor 客户端 JavaScript 源文件 | src/Components/Web.JS,编译产物随 Web 程序集一起分发 |
WebAssembly | WebAssembly 托管模型特有实现 | src/Components/WebAssembly,内部再分多个子项目(详见下文) |
WebView | 支撑dotnet/maui中Blazor 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 从目录层级理解"共享"
Web、Server、WebAssembly三者位于同一层级,都是"平台侧"实现;而Components(核心组件模型)、Forms、Authorization等则属于"模型层"的共享能力。这样组织的好处是:当你新写一个.razor组件时,面向的 API 一律来自共享模型层;运行在哪个托管模型下,只是编译/打包时选择挂接Web、Server还是WebAssembly平台程序集的问题。
三、从源码构建 Components 模块
README 明确提示:若要从源码构建其他具体项目,应先参考 从源码构建的文档(其"Step 3: Build the repo"小节)。在此基础上,构建 Components 模块按以下步骤进行。
3.1 前置条件与仓库清理
安装 Node.js:Components 的 JS 资产需要 npm 处理。
清理仓库:强烈建议在切换分支或更新工作分支后,先移除上一次构建残留的任何资产:
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 还原依赖与生成资产
还原 JS 模块:使用 npm 的离线模式还原,无需外网——依赖源来自仓库的子模块(sub-module):
npm ci --offline还原 .NET 依赖并初始化仓库:执行仓库根目录下的还原脚本(Linux/macOS 用
./restore.sh,Windows 用./restore.cmd):# Linux 或 Mac ./restore.sh# Windows ./restore.cmd构建仓库所需的全部 JS 资产(如 SignalR 等),运行:
npm run build构建 Components 本体:README 给出的命令为
./src/Components/build.cmd;构建产物完成后,可选用 Visual Studio 打开组件工程:仓库根目录提供了Components.slnf与ComponentsNoDeps.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.razor、AsyncDisposableComponent.razor、ConcurrentRenderParent.razor、VirtualizationComponent.razor; - 事件/绑定类:
EventBubblingComponent.razor、BindCasesComponent.razor、TouchEventComponent.razor、MouseEventComponent.razor; - 互操作类:
InteropComponent.razor、DotNetToJSInterop.razor、JavaScriptRootComponents.razor; - 路由/导航/状态类:
RouterTest/、PreserveStateComponent.razor、PrerenderedToInteractiveTransition.razor; - 表单/级联/模板类:
FormsTest/、CascadingValueTest/、TemplatedTable.razor等。
其目录名几乎覆盖了组件模型的全部公开能力——这正是用"同一份被测应用 × 多种宿主"验证"模型是共享的"这一核心命题的实证。
- 生命周期/渲染类:
在 CI 中,这些 E2E 测试作为aspnetcore-components-e2e管道的一部分运行。
4.2 命令行运行 E2E 测试
完成第三节的构建步骤后,按如下方式执行:
激活本地安装的 .NET:
# Linux 或 Mac source activate.sh# Windows . ./activate.ps1启动测试:
dotnet test ./src/Components/test/E2ETest按名称过滤:可用
--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.razor、DynamicComponentRendering.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),仅供参考