65.9K
CodeProject 正在变化。 阅读更多。
Home

高级 Blazor:共享程序集和从 Edge 进行调试

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2019 年 11 月 20 日

CPOL

7分钟阅读

viewsIcon

5973

涵盖了高级 Blazor 技术,例如在 Blazor WebAssembly 项目和 Blazor 服务器端项目之间共享 Razor 类库。展示了如何引用嵌入式资源,如 JavaScript 库,如何在服务器端进行调试,然后将其部署为客户端并在浏览器中进行调试。

人们对 Blazor WebAssembly 表现出极大的兴趣。开发人员终于可以使用他们选择的语言(例如 C#)来构建基于浏览器的单页应用程序 (SPA)。在撰写本文时,Blazor WebAssembly 仍处于预览状态。计划于 2020 年 5 月发布,届时团队将致力于添加功能并改善开发人员体验。Blazor WebAssembly 项目最大的挑战之一是缺乏集成的调试功能。

Blazor debug

确实存在一个正在缓慢发展的调试体验。但在此期间,您可以遵循一些简单的步骤和实践来让生活更轻松。

使用 Razor

我遵循“最低公分母”开发理念。我将可以广泛共享的内容提取到单独的组件中,并努力使逻辑与框架实现分离。例如,我更喜欢使用 Blazor MVVM 模式,而不是将逻辑直接嵌入到 Razor 组件中。这允许我使用可以在 Blazor、ASP.NET MVC、WPF 甚至 Xamarin(移动)项目之间共享的类库。下一层是视图,而通过 Razor 类库,用户界面 (UI) 也可以共享!

不要直接在您的 Blazor 项目中构建 Razor 组件,请考虑将其放入 Razor 类库中。Razor 类库不仅限于原生组件;您还可以嵌入 JavaScript、图像和其他共享资源。本文中的代码全部包含在一个示例应用程序中

 JeremyLikness/AdvancedBlazor

首先,我创建了一个名为 BlazorApp.Shared 的 Razor 类库。项目模板会生成一些默认文件,我用几个自定义组件覆盖了它们。默认情况下,会嵌入一个 JavaScript 文件,该文件会从提示中返回文本。

window.exampleJsFunctions = {
   showPrompt: function (message) {
      return prompt(message, 'Type anything here');
   }
};

一个名为 ExampleJsInterop 的类提供了一个 JavaScript 调用包装器。

public class ExampleJsInterop
{
   public static ValueTask<string> Prompt(IJSRuntime jsRuntime, string message)
   {
      // Implemented in exampleJsInterop.js
      return jsRuntime.InvokeAsync<string>(
         "exampleJsFunctions.showPrompt",
         message);
   }
}

为了方便在项目中的任何位置使用 JavaScript 互操作,我在 _Imports.razor 中添加了一个 using 语句

@using Microsoft.JSInterop

接下来,我创建了一个名为 Prompter.razor 的组件,该组件会在按钮点击时显示提示并更新一些文本。模板如下所示

<div class="my-component">
This Blazor component is defined in the <strong>BlazorApp.Shared</strong> package.
</div>
<button @onclick="GetText">Enter Text</button>
<p>Prompt text: @Text</p>

代码隐藏文件使用 JavaScript 提示来检索文本并更新类中的属性。

public string Text { get; set; } = "Click the button to change this.";
public async void GetText()
{
   Text = await ExampleJsInterop.Prompt(JsRuntime, "Enter your text:");
   StateHasChanged();
}

我在此示例中使用模板生成的代码。比使用静态方法更好的实现方式是提供一个具有方法签名的接口,并将实现注册为 Startup 类中的单例,然后在需要时注入。这将使编写单元测试更加容易。

这个简单组件的目的是说明我如何编写自定义 JavaScript 并将其打包到一个共享库中。我创建了另一个名为 ShowHost 的组件,该组件显示日期和 System.Environment.OSVersion 报告的主机(OS)平台。稍后在这篇博文中您将明白原因。模板

<h3>Your environment is: @OS (last refreshed at: @Refresh)</h3>
<button @onclick="DoRefresh">Refresh Environment</button>

代码隐藏部分

public string OS { get; set;} = "Not Set";
public string Refresh { get; set; } = "never";
protected override void OnInitialized()
{
   DoRefresh();
}
public void DoRefresh()
{
   OS = Environment.OSVersion.VersionString;
   Refresh = DateTime.Now.ToString();
}

最后,我添加了一个 Wrapper 控件来包装其他组件,这样我只需要在一个 Blazor 项目中放置一个组件。如果将来扩展示例,我可以在 Wrapper 中嵌入其他组件,而无需更改我的宿主项目。Wrapper 组件有两个 Prompter 实例,以显示它与多个实例一起正常工作。

<h3>Wrapper</h3>
<h4>Environment:</h4>
<ShowHost></ShowHost>
<h4>First prompt:</h4>
<Prompter></Prompter>
<h4>Second prompt:</h4>
<Prompter></Prompter>

部署您的 Blazor

Razor 类库本身并不能做太多事情。它需要一个宿主项目。为了增加一点变化,我创建的第一个项目是一个服务器端项目,而不是宿主 Blazor WebAssembly 项目。

事实证明,Blazor 有几种不同的托管模型。尽管 WebAssembly 仍处于预览状态,但 Blazor Server 托管模型已准备好投入生产。您可以在 官方文档中了解更多信息。我个人是 WebAssembly 的爱好者,但使用服务器端项目有几个原因。首先,它可以引用与 Blazor WebAssembly 使用的相同的 Razor 类库。其次,它提供了完全集成的调试体验。只需设置您的断点,然后按 F5 即可开始!

在名为 BlazorApp.Server 的项目中,我添加了对 BlazorApp.Shared 的引用。接下来,我需要确保 JavaScript 文件在我的应用程序中正确加载。幸运的是,Razor 类库公开了一个非常简单的约定来访问嵌入式资源。相对于网页,您可以使用约定访问资源:_content/{assemblyname}/{filename}。要包含脚本,只需将以下 script 标签添加到 _Host.cshtml

<script src="_content/BlazorApp.Shared/exampleJsInterop.js"></script>

这是一个手动步骤,必须为任何其他文件重复此操作。Blazor 团队之所以不自动包含资源,是因为资源和命名冲突的可能性。我有一些关于使该过程更具“自动发现性”的想法,我将在后续帖子中进行探讨。如果您有充分的理由希望此功能“开箱即用”,请在下方的评论中告诉我,我将与团队分享您的反馈。

接下来,我打开了 Index.razor 并放入了 Wrapper 组件

@using BlazorApp.Shared
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<Wrapper></Wrapper>

要“轻松地”调试,我在 Prompter 组件的 StateHasChanged() 行上设置了一个断点,然后按 F5。我的应用程序会渲染我的 OS 版本(请注意这是我的“服务器”或笔记本电脑版本)、日期和按钮。

Server-side Blazor app

点击“Enter Text”按钮之一会显示 JavaScript 提示,表明嵌入共享资源和 JavaScript 互操作都正常工作。输入文本并点击“OK”后,断点会被命中,我可以检查变量、修改属性、查看调用堆栈并执行几乎任何我想要的调试任务。现在代码看起来不错了,是时候创建 Blazor WebAssembly 项目了!

调试客户端(Edge 版本)

创建了一个名为 BlazorApp.Client 的 Blazor WebAssembly 项目后,我添加了对与服务器端应用程序相同的 Razor 类库(BlazorApp.Shared)的引用。我使用了完全相同的约定来引用 JavaScript,只是这次我将 script 标签放在 wwwroot 下的 index.html 中,而不是 _Host.cshtmlIndex.razor 的修改与服务器端版本完全相同。我将启动项目设置为客户端,然后按 CTRL+F5 启动它。结果看起来几乎相同……只有一个例外。您能立即看到吗?

Client-side Blazor

如果您错过了,它报告的“平台”是 Unix 1.0.0.0。这是因为应用程序现在完全在浏览器中运行,通过 WebAssembly 在 Mono 上运行。其余功能大致相同。

如果现在需要调试应用程序怎么办?如果您打开调试控制台,您会看到类似这样的消息

调试快捷键:Shift+Alt+D(当应用程序获得焦点时)

很可能按下神奇的组合键会导致错误,并附带有关如何修复它的说明。

Debug instructions

如果您使用的是 Chrome,您就可以了。只需注意您需要关闭所有现有的 Chrome 窗口,如果您在任务栏中有 Chrome 加速器,也需要退出它。

但如果您使用 Edge 怎么办?没问题!关闭所有打开的 Edge 实例,然后运行以下命令(我只更改了路径以指向我的 Edge 安装,您的可能不同)

"%programfiles(x86)%\Microsoft\Edge Dev\Application\msedge.exe" --remote-debugging-port=9222 https://:57955

不确定您的 Edge 安装路径?没问题。只需右键单击您用于启动它的图标,然后进入属性。您应该会看到一个包含路径的属性,或者一个包含可执行文件完整路径的“目标”属性的快捷方式选项卡。

如果您的应用程序运行在不同的端口上,请相应地更改 URL。这应该会再次启动应用程序。按下 SHIFT+ALT+D 组合键,会打开一个新选项卡,其中包含一些额外信息。这是我的样子

Edge debug view

您可以展开 file:// 下的虚拟文件系统来导航您的源代码,并在与服务器端示例中使用的相同的 StateHasChanged() 行上设置断点。切换回主选项卡,单击提示,输入一些文本并点击“OK”。您应该会看到“paused in debugger”(在调试器中暂停)的消息。转到调试选项卡,您现在可以看到断点已被触发。可以展开“Scope”下的右侧面板来检查变量和属性的值。

Client debug experience

这就是魔术的结束!

在这篇博文中,我们探讨了

  • 使用 Razor 类库来提高代码的可移植性
  • 实现 Blazor Server 项目以进行高级调试
  • 从 Edge 浏览器调试 Blazor WebAssembly

您可以从 GitHub 下载该项目

 JeremyLikness/AdvancedBlazor

一如既往,我非常乐意听取您的想法和反馈。在下方开始或加入讨论!

此致,

Jeremy Likness

高级 Blazor:共享程序集和从 Edge 进行调试 - CodeProject - 代码之家
© . All rights reserved.