公告

VS Code 中的 Blazor 与 Razor:为 .razor 文件提供 C# 工具

抛光金属 @ 雕塑燃起琥珀色火焰,立于青色光线勾勒的层叠玻璃之上

SharpLsp 现在理解 Razor。Blazor 组件(.razor 文件),以及 Razor Pages 和 MVC 视图(.cshtml 文件),与你的其余 C# 代码使用同一个 Roslyn 引擎。在标记内部,你可以使用悬停、补全、诊断、转到定义、引用和重命名。这项支持随 SharpLsp 0.30 发布。

下面是一个小型 Blazor Web App 上的效果:

带旁白、原创配乐和字幕。在 VS Code 中基于一个 Blazor Web App 录制:标记中的 Roslyn 悬停、Razor 表达式中的补全、HTML 补全与悬停、显示在对应 Razor 行上的 C# 错误、按 F12 跳进 @code 代码块、查找引用与重命名、从 Program.cs 按 F12 打开 App.razor,最后是链接标签编辑和折叠。 在单独页面观看

什么是 Blazor?一分钟了解 Blazor 与 Razor

这两个名字描述的是不同的层次,所以很容易混淆。

在这三者之下是一个 Razor 编译器,它把每个 Razor 文件转换成 C#。这就是为什么 SharpLsp 中的一套实现就能同时服务 Blazor Server、Blazor WebAssembly、Razor Pages 和 MVC。

在 .razor 文件中你能得到什么

下面的一切都展示在视频演示的 Basket.razor 页面上,每一项都有自动化测试覆盖。

围绕 C# 的标记仍然是 HTML。在 VS Code 中,SharpLsp 把元素和属性补全以及 HTML 悬停转发给 VS Code 自带的 HTML 语言服务。它发送的是文件中每个 C# 片段都被留空的 HTML 视图,因此 HTML 服务永远不会把 C# 误认为标记。

工作原理:Razor 编译器是唯一的事实来源

构建 Razor 工具时,一个诱人的做法是写一个 Razor 解析器。SharpLsp 没有这样做。Razor 的语义属于 Razor 编译器,任何第二套解析器都会在每个版本中与它偏离。

自 .NET 6 起,Razor 编译器就是一个 C# 源生成器。它随 .NET SDK 一起提供,C# sidecar 的设计时构建会像 dotnet build 一样运行它。生成器把每个 Razor 文件转换成一个 C# 类。每个 C# 片段都被原样复制进该类,位于一个指回 Razor 文本的增强 #line 指令之中。

SharpLsp 根据这些指令建立双向映射:

  1. Razor 位置上的请求会映射到生成的 C# 中相同的字符。
  2. Roslyn 在那里作答,与处理 .cs 文件时完全相同。
  3. 答案中的每个位置都映射回 Razor 文件。

映射从不盲目相信指令。Roslyn 会把每个已映射范围的结束位置报告为片段末尾再多一个字符,因此 SharpLsp 通过逐字符比较 Razor 文本和生成的文本,来测量每个片段。生成器自己写出的代码(例如呈现标记的 __builder 调用)中的位置没有对应的 Razor 文本,永远不会传到你这里。

由于生成器来自编译器本身,它映射的每个构造中的 C# 都得到同样的处理。这包括 @bind 值、事件处理程序、泛型组件和 @code 块。负责折叠、大纲、链接编辑和 HTML 视图的标记一侧,来自编译器公开的中间表示,而不是重新实现。

在构建过程中发现的问题

在一个全新的 dotnet new blazor 应用上运行真实宿主时,发现了一个误报:_Imports.razor 中的每个 @using 都被淡显为不必要。Roslyn 只能看到每个文件自己生成的 C#,因此它无法看到导入文件的 using 是服务于它旁边和下方的组件的。当组件自身的 using 只被其标记需要时,也存在同样的问题。现在 Razor 文件不再报告不必要的 using,而 C# 文件(包括代码隐藏)仍然会报告。

第二个发现与 SDK 版本有关。Razor 生成器来自构建你的项目的 SDK,也就是已安装的最新版本,除非 global.json 固定了另一个版本。SDK 10.0.401 的生成器需要比 SharpLsp 捆绑的 Roslyn 更新的版本,因此起初它的 Razor 文件得不到任何 C# 功能(issue #399)。现在,SharpLsp 会加载自己捆绑的 Razor 编译器来代替该生成器,并记录一条写明两个 Roslyn 版本的警告,因此较新的 SDK 无需固定版本也能正常工作。详细信息请参阅 Blazor 与 Razor 文档。

.NET 10 上的 Blazor:Server、WebAssembly 和 Razor 类库

SharpLsp 读取你的解决方案中已有的项目。它们都通过 Microsoft.NET.Sdk.Web、Microsoft.NET.Sdk.BlazorWebAssembly 或 Microsoft.NET.Sdk.Razor 以相同的方式加载:

在 .NET 10 SDK 上的 ASP.NET Core 10 已经过验证,包括一个全新的 dotnet new blazor 应用。net8.0 和 net9.0 上的项目通过同一个源生成器编译 Razor,因此应该可以工作,但我们还没有验证它们。早于 .NET 6 的项目在不使用源生成器的情况下编译 Razor,因此不受支持,.NET Framework 的 MVC 5 和 Web Forms 同样不受支持。

接下来

这个版本涵盖 Razor 文件中的 C#。接下来是标记本身,在 issue #397 中追踪:

试一试

  1. 从 VS Code Marketplace 安装 SharpLsp,或在 Cursor、Windsurf 和 VSCodium 中从 Open VSX 安装。Razor 支持需要 0.30 或更高版本。
  2. 用 dotnet new blazor -o BlazorDemo 创建一个 Blazor Web App,然后打开该文件夹。
  3. 打开 Components/Pages/Counter.razor,悬停 currentCount。

Blazor 与 Razor 文档列出了每一项功能、支持的项目以及尚不支持的内容。

进一步阅读