如果您一直在了解 .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 服務器應用程序?
Blazor 服務器應用在服務器上運行,在那里它們享受完整 .NET Core 運行時的支持。所有處理都在服務器上完成,UI/DOM 更改通過 SignalR 連接傳輸回客戶端。這種雙向 SignalR 連接是在用戶第一次在瀏覽器中加載應用程序時建立的。由於您的 .NET 代碼已經在服務器上運行,因此您無需為前端創建 API。您可以直接訪問服務、數據庫等,在傳統的服務器端技術上做任何想做的事情。
何時使用 Blazor 服務器
- 當您想要在完整的 .NET Core 運行時上運行您的應用程序時
- 當您希望將應用程序的初始下載大小保持在很小的時候
- 當你想保持你的應用程序啟動時間非常快時
- 當您希望將應用程序的代碼保留在服務器上並且不希望將其下載到客戶端時。
- 當您希望應用程序的快速開發周期且現有 .NET 開發人員幾乎沒有學習曲線時
- 當您想讓您的應用對搜索引擎友好時
- 當您希望您的應用程序在不依賴於 WebAssembly 的舊瀏覽器上運行時
- 當您想在 Visual Studio 中像任何普通 .NET應用程序一樣調試 .NET 代碼時
- 當您想要構建 Intranet 或低需求面向公眾的應用程序時
何時不使用 Blazor 服務器
- 當您的應用程序在高延遲環境中運行時
- 當您希望您的應用在沒有與服務器保持 SignalR 連接的情況下脫機工作時
- 當您不想增加服務器資源來處理大量連接的 SignalR 客戶端時。
什么是 Blazor WebAssembly 應用程序?
這種托管模型是現代流行的 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 太多而無法在有效負載上妥協時
- 當您無法在緩慢的啟動時間上妥協時,尤其是在互聯網連接不佳的情況下
- 當您無法妥協這一事實時,該應用程序必須在具有所有安全限制的瀏覽器沙箱中運行。
在 Visual Studio 2019 中創建 Blazor 服務器應用程序
打開 Visual Studio 2019 社區版,然后單擊創建新項目選項。從可用模板列表中選擇 Blazor 應用程序模板,然后單擊下一步。
提供項目名稱,例如 BlazorServerApp,然后單擊下一步。您將看到以下對話框,要求您選擇要創建的 Blazor 應用程序類型。我們正在創建 Blazor 服務器應用程序,因此選擇 Blazor 服務器應用程序並單擊創建按鈕。
Visual Studio 將使用解決方案資源管理器中顯示的以下文件夾和文件為我們創建 Blazor 服務器應用程序。
讓我們了解 Blazor 服務器應用程序中可用的一些重要文件和文件夾。
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 服務器應用程序相關的服務。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
我們在 Configure 方法中還有以下兩行重要的代碼。 MapBlazorHub 方法配置 Blazor 服務器應用程序所需的 SignalR 中心終結點。 MapFallbackToPage 方法將所有這些請求映射到 _Host 頁面,這些頁面沒有映射到任何控制器、razor頁面等。這將允許所有動態內容請求路由到 SPA 框架而不是拋出 404 Not Found。
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
_Host.cshtml
這是應用程序的根頁面,每個 Razor 組件/頁面都將在此主機頁面中呈現。它具有基本的 HTML 元素,例如 html、head 和 body,以及一些特殊元素。請注意,Blazor 是一個基於組件的框架,Blazor 中的一切都是一個組件。指定了我們想要呈現應用程序根組件的位置。
<component type="typeof(App)" render-mode="ServerPrerendered" />
此文件還在末尾注入 blazor.server.js 文件,此 JavaScript 文件包含設置 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 and 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 應用程序還有一個包含共享組件的共享文件夾。這些組件可以被整個應用程序中的任何組件使用,就像我們上面看到的 SurveyPrompt 組件一樣。 Shared 文件夾中另一個有趣的共享組件是 NavMenu 組件,它呈現我們的 Blazor 服務器應用程序的頂部導航欄。
_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 服務器應用程序並在瀏覽器中查看它的運行情況。在 Visual Studio 中按 F5,您將看到一個漂亮的默認 Blazor 服務器應用程序。嘗試從側邊欄導航到不同的頁面,並嘗試在 Counter 頁面上使用計數器,您會注意到沒有頁面刷新或回發到服務器。一切都感覺流暢和快速,就像典型的 SPA 一樣,所有瀏覽器和服務器的通信都是使用 SignalR 連接進行的。
您還可以打開瀏覽器開發人員工具,您會注意到包括 blazor.server.js 文件在內的所有標准 CSS 和 JavaScript 文件都已下載到客戶端,並通過 Web 套接字建立了 SignalR 連接。
在 Visual Studio 2019 中創建 Blazor WebAssembly 應用程序
我們已經了解了 Blazor 服務器應用程序的基礎知識,並在瀏覽器中看到了它的運行情況。現在讓我們創建一個 Blazor WebAssembly 應用程序,以便我們可以看到不同之處。按照我們上面提到的相同步驟,並使用 Blazor 應用程序模板在 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 應用程序的構建塊,無論我們使用什么托管模型,我們都可以以類似的方式構建這些組件。如果您喜歡這篇文章,請分享它並傳播知識。