翻译自 Waqas Anwar 2021 年 3 月 12 日的文章 《A Beginner’s Guide To Blazor Server and WebAssembly Applications》

如果您一直緊跟 .net 世界的最新發展趨勢,那麼現在您一定聽說過 blazor。目前在 .net 社區中有很多關於 blazor 的宣傳,這種宣傳最常見的原因是它引入了一些大多數 .net 開發人員十幾年來一直夢寐以求的東西,即:既可以在服務端又可以在瀏覽器中運行 c# 的能力。blazor 允許我們使用 html、css 和 c#(而不是 javascript)來構建交互式 web 應用程式。在本教程中,我將居間 blazor 的基本概念,並概述可用於 blazor 的不同的託管模型。我還將居間每種託管模型的優缺點,以便您可以為下一個 blazor 項目託管模型做出最佳的決定。
blazor 是什麼?
blazor 是一個免費、開源的單頁應用程式(spa)開發框架,使開發人員能夠在服務端和客戶端上使用 c# 構建交互式 web 應用程式。blazor 不需要在客戶端上安裝任何插件來在瀏覽器中執行 c#/.net 代碼。它使用 webassembly 執行 .net 代碼,webassembly 是所有主流瀏覽器都支持的 web 標準。blazor 還可以在服務端運行 .net 代碼並構建 ui,然後通過 signalr 連接僅將更新的 dom 傳輸到客戶端。

webassembly 是什麼?
webassembly(有時簡寫為 wasm)是一種可移植的二進位格式(低級指令集),被設計用於在任何能夠解釋這些指令的主機上運行。webassembly 的主要目標是允許開發人員構建高性能的 web 應用程式,但其格式也被設計為可執行於和集成到其他環境中。webassembly 目前受到了所有主流瀏覽器的支持,比如 chrome、android 版 chrome、edge、firefox、safari、opera 等。

blazor 託管模型
blazor 組件模型是 blazor 的核心,它的設計方式使計算 ui 更改和呈現 ui 彼此分離。這就是為什麼無論您使用何種方式渲染您的應用程式,基本的組件模型都保持不變的原因。在撰寫本文時,有四種渲染/託管模型可用,它們都處於不同的開發階段。
- Blazor Server
- Blazor WebAssembly
- Blazor Electron
- Mobile Blazor Bindings
blazor electron 和 mobile blazor bindings 目前處於實驗階段,microsoft 尚未承諾發布這些託管模型,因此我不會在本文中討論它們。
blazor server app 是什麼?
blazor server 應用程式在伺服器上運行,可享受完整的 .net core 運行時支持。所有處理都在伺服器上完成,ui/dom 更改通過 signalr 連接回傳給客戶端。這種雙向 signalr 連接是在用戶第一次從瀏覽器中加載應用程式時建立的。由於 .net 代碼已經在伺服器上運行,因此您無需為前端創建 api。您可以直接訪問服務、資料庫等,並在傳統的服務端技術上做任何您想做的事情。

§何時使用 blazor server
- 當您想在完整的 .net core 運行時上運行應用程式時
- 當您想要保持應用程式的初始下載大小非常小時
- 當您想保持應用啟動時間非常快時
- 當您想把應用程式的代碼保留在伺服器上,而不希望它被下載到客戶端時
- 當您想要一個快速的應用開發周期,而現有的 .net 開發人員幾乎不需要學習曲線時
- 當您想讓您的應用對搜尋引擎友好時
- 當您希望應用程式在舊的瀏覽器上運行,而不依賴於 webassembly 時
- 當您想在 visual studio 中像調試普通 .net 應用程式一樣調試 .net 代碼時
- 當您想要構建內部網或低需求的面向公眾的應用程式時
§何時不要使用 blazor server
- 當您的應用程式在高延遲環境中運行時
- 當您希望您的應用程式離線工作,而不需要一個與伺服器的固定 signalr 連接時
- 當您不想增加伺服器資源以響應大量連接的 signalr 客戶端時
blazor webassembly app 是什麼?
這種託管模型是現代流行的 spa 框架(比如 angular、vue 和 react)的直接競爭對手,也是大多數開發人員有興趣學習 blazor 的主要原因。它允許開發人員使用 c# 取代 javascript 編寫所有前端 ui 邏輯。在這種託管模型中,第一次請求時,會將應用程式的 dll 及其所有依賴項和小尺寸的 mono .net 運行時下載到客戶端。然後,客戶端中的 mono 運行時就會加載並執行應用程式代碼。blazor webassembly 程式可以用 c、c# 等其他語言編寫,然後編譯成 webassembly 字節碼。

§何時使用 blazor webassembly
- 當您想要將整個應用程式編譯為靜態文件,並將它們提供給客戶端,而不需要伺服器上的 .net 運行時時。這意味著您的後端可以用 php、node 或 rails 編寫,並服務於用 blazor 編寫的前端應用程式。
- 當您想要構建可以在客戶端脫機運行而無需持續連接到服務端的應用程式時。
- 當您想要將處理轉移到客戶端並減少服務端的負載時。
- 當您想在客戶端和服務端之間共享代碼和庫時。
§何時不要使用 blazor webassembly
- 當由於下載到客戶端的文件/dll 太多,而您無法在有效負載上妥協時。
- 當您無法在緩慢的啟動時間上(特別是在網絡連接不佳的情況下)妥協時。
- 當您無法妥協於應用程式必須運行在具有完整的安全限制的瀏覽器沙箱環境中時。
為了更好地理解 blazor 託管模型,讓我們在 visual studio 2019 中分別創建一個 blazor server 和 blazor webassembly 應用程式。
在 visual studio 2019 中創建 blazor server 應用
打開 visual studio 2019 並點擊創建新項目。從可用的模板列表中選擇 blazor app 模板並點擊下一步。

指定項目名稱(比如 blazorserverapp)並點擊下一步。您將看到下面的對話框,詢問您選擇要創建的 blazor 應用程式的類型。我們要創建 blazor server 應用,所以請選擇 blazor server app 並點擊 創建 按鈕。

visual studio 將為我們創建一個 blazor server 應用程式,其中在解決方案資源管理器中包含以下文件夾和文件。

讓我們來討論一下 blazor server app 中可用的一些重要文件和文件夾。
§Program.cs
這個文件包含 main 方法,它是項目的入口點。main 方法中調用 createhostbuilder 方法,為我們配置默認的 asp.net core 宿主。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<startup>();
});
}
§Startup.cs
它与我们在标准 ASP.NET Core 项目中使用的文件相同。需要重点注意的一点是 ConfigureServices 方法中调用了 AddServerSideBlazor,该方法添加 Blazor Server App 相关的服务。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<weatherforecastservice>();
}
在此文件的 Configure 方法中我们还有以下两行重要的代码。MapBlazorHub 方法配置 Blazor Server App 所需的 SignalR Hub Endpoints。MapFallbackToPage 方法会将所有未与任何控制器、razor 页面等匹配的请求映射到 _Host 页面,这将允许所有动态内容请求路由到 SPA 框架,而不是抛出 404 Not Found。
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
§_Host.cshtml
这是应用程序的根页面,每个 Razor 组件/页面都将在此 host 页面中呈现。它具有基本的 HTML 元素,例如 html、head 和 body,以及一些特殊元素。请注意,Blazor 是一个基于组件的框架,Blazor 中的每一内容都是一个组件。<component> 指定了我们想让应用程序根组件呈现的位置。
<component type="typeof(App)" render-mode="ServerPrerendered"></component>
该文件还在末尾注入了 blazor.server.js 文件,此 JavaScript 文件包含设置 SignalR 连接到服务端的代码。此连接在浏览器加载应用程序后立即建立,然后被用于服务端和客户端浏览器之间的实时通信。如果您想了解有关 SignalR 的更多知识,请阅读我的文章 Display Live Sports Updates using ASP.NET Core SignalR。
<script src="_framework/blazor.server.js"></script>
§App.razor
這是 blazor app 的主要組件,其主要工作是攔截路由並呈現 found 或 notfound 組件。如果找到與路由匹配的組件,則呈現 found 組件,如果未找到匹配的組件,則呈現 notfound 組件。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
§MainLayout.cshtml
mainlayout 文件包含應用程式的主布局,其標記可以被多個 razor 組件共享。這個布局組件通常包含應用程式的常見 ui 元素,例如頁眉、菜單、頁腳、側邊欄等。為我們生成的默認 mainlayout 有一個側邊欄,用來渲染 navmenu 組件,它還使用 razor 語法 @body 來指定其他組件的內容將在布局標記中呈現的位置。
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<div class="content px-4">@Body</div>
</div>
</div>
§wwwroot 文件夾
該文件夾包含靜態文件,例如圖片、字體、圖標、css 和 javascript 文件等。
§pages 和 shared 文件夾
該文件夾包含我們之前討論過的 _host.cshtml 文件以及一些 razor 組件。blazor 應用程式是具有 .razor 擴展名的 razor 組件的集合。其中一些組件稱為可路由組件,因為可以使用其路由訪問它們。例如,當我們導航到應用程式根 url 時,將呈現下面的 index.razor 組件。該 url 是使用 index.razor 組件頂部的 @page 指令指定的。
§Index.razor
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
請注意,上面的頁面還使用了一個子組件 surveyprompt,之所以稱它為子組件,是因為它沒有 @page 指令,它可以被嵌入到其他組件中。
pages 文件夾中還有一些其他的 razor 組件,這些組件都可以使用文件頂部指定的路徑進行訪問。例如,當我們導航到 /counter 路徑時,counter 組件將呈現。類似地,fetchdata 組件將使用 /fetchdata 路徑呈現。
razor server 應用程式還有一個包含共享組件的 shared 文件夾。這些組件可以被整個應用程式中的任何組件使用,就像我們上面看到的 surveyprompt 組件一樣。shared 文件夾中另一個有趣的共享組件是 navmenu 組件,它呈現 blazor server 應用程式的頂部導航欄。
§_Imports.razor
該文件類似於我們在 asp.net mvc web 應用程式中的 _viewimports.cshtml 文件,它包含我們可以在不同 razor 組件中使用的命名空間列表。在 _imports.razor 文件中聲明所有這些命名空間的好處是我們不需要在每個 razor 組件中重複引入它們。
@using System.Net.Http @using Microsoft.AspNetCore.Authorization @using
Microsoft.AspNetCore.Components.Authorization @using
Microsoft.AspNetCore.Components.Forms @using
Microsoft.AspNetCore.Components.Routing @using
Microsoft.AspNetCore.Components.Web @using
Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop
现在是时候运行我们的 Blazor Server 应用程序并在浏览器中查看它的运行情况了。在 Visual Studio 中按 F5,您将看到一个漂亮的默认 Blazor Server 应用程序。试试从侧边栏导航到不同的页面,并尝试在 Counter 页面上使用计数器,您会注意到没有页面刷新或回传到服务器。一切都像经典的 SPA 那样流畅和快速,浏览器和服务端的所有通信都是使用 SignalR 连接进行的。

您也可以打開瀏覽器開發者工具,您會注意到所有標準的 css 和 javascript 文件(包括 blazor.server.js 文件)都下載到了客戶端,並通過 web sockets 建立了一個 signalr 連接。

在 visual studio 2019 中 創建 blazor webassembly 應用
我們已經了解了 blazor server app 的基礎知識,並在瀏覽器中看到了它的運行情況。現在讓我們創建一個 blazor webassembly app,以便我們可以理解它們的不同之處。按照我們上面提到的相同步驟,並使用 blazor app 模板在 visual studio 中創建一個新的 blazor 應用程式。當您被詢問選擇 blazor app 的類型時,這次需要選擇 blazor webassembly app。

visual studio 將為我們創建一個 blazor webassembly 應用程式,其中在解決方案資源管理器中包含以下文件夾和文件。

您可以輕鬆發現這兩種類型的應用程式之間的一些差異。例如,在 blazor webassembly 應用程式中沒有以下文件:
- _Host.cshtml
- Error.cshtml
- Startup.cs
- appsettings.json
§index.html
在 blazor webassembly 應用程式中,我們會在 wwwroot 文件夾中有一個 index.html 文件,作為主頁面, 該文件在末尾注入了 blazor.webassembly.js 文件,此文件由框架提供以處理下載 .net 運行時、blazor 應用程式及其所有依賴項。它還包含為了運行應用而初始化運行時的代碼。
§Program.cs
在 blazor webassembly 應用程式中,應用程式的根組件在 program.cs 文件中的 main 方法中指定。應用程式的根組件是 app.razor,你可以看到它是如何被添加到 rootcomponents 集合中的。
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<app>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
}
}
在 Visual Studio 中按 F5,您将看到一个相似的 Blazor WebAssembly 应用程序。尝试从侧边栏导航到不同的页面,并尝试像之前在 Blazor Server App 中所做的那样在 Counter 页面上使用计数器。 一切看起来感觉一模一样,也没有服务器端回传。

正如我們已經知道的那樣,blazor webassembly 應用程式會在客戶端下載應用程式及其所有依賴項,因此如果您打開瀏覽器開發者工具,會看到客戶端下載了大量 dll(只會在第一次瀏覽時下載)。

以上所有文件只會在第一次請求時下載,然後它們被緩存在瀏覽器中。如果您再次刷新頁面,將會看到這一次下載的文件很少。

總結
在本文中,我試圖為您居間 blazor spa 框架的基本概念,我們看到了兩個 blazor 應用程式使用兩種不同的託管模型進行託管。因為 blazor 框架嚴重依賴於 razor 組件,所以兩個項目中的大部分代碼和文件都是相同的。這些組件是 blazor 應用程式的構建塊,無論使用哪種託管模型,我們都可以以相似的方式構建這些組件。如果您喜歡本文,請分享它並傳播知識。
作者: waqas anwar
翻译: 技术译站
链接: 英文原文