Blazor Server 和 WebAssembly 應用程序入門指南

翻譯自 Waqas Anwar 2021年3月12日的文章 《A Beginner』s Guide To Blazor Server and WebAssembly Applications》 [1]

A-Beginner-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 傳輸到客戶端。

BLAZOR

WebAssembly 是什麼?

WebAssembly(有時簡寫為 Wasm)是一種可移植的二進制格式(低級指令集),被設計用於在任何能夠解釋這些指令的主機上運行。WebAssembly 的主要目標是允許開發人員構建高性能的 Web 應用程序,但其格式也被設計為可執行於和集成到其他環境中。WebAssembly 目前受到了所有主流瀏覽器的支持,比如 Chrome、Android 版 Chrome、Edge、Firefox、Safari、Opera 等。

WebAssembly

Blazor 託管模型

Blazor 組件模型是 Blazor 的核心,它的設計方式使計算 UI 更改和呈現 UI 彼此分離。 這就是為什麼無論您使用何種方式渲染您的應用程序,基本的組件模型都保持不變的原因。 在撰寫本文時,有四種渲染/託管模型可用,它們都處於不同的開發階段。

  1. Blazor Server
  2. Blazor WebAssembly
  3. Blazor Electron
  4. Mobile Blazor Bindings

Blazor Electron 和 Mobile Blazor Bindings 目前處於實驗階段,Microsoft 尚未承諾發佈這些託管模型,因此我不會在本文中討論它們。

Blazor Server App 是什麼?

Blazor Server 應用程序在服務器上運行,可享受完整的 .NET Core 運行時支持。所有處理都在服務器上完成,UI/DOM 更改通過 SignalR 連接回傳給客戶端。這種雙向 SignalR 連接是在用戶第一次從瀏覽器中加載應用程序時建立的。 由於 .NET 代碼已經在服務器上運行,因此您無需為前端創建 API。您可以直接訪問服務、數據庫等,並在傳統的服務端技術上做任何您想做的事情。

Blazor-Server-Apps

何時使用 Blazor Server

  1. 當您想在完整的 .NET Core 運行時上運行應用程序時
  2. 當您想要保持應用程序的初始下載大小非常小時
  3. 當您想保持應用啟動時間非常快時
  4. 當您想把應用程序的代碼保留在服務器上,而不希望它被下載到客戶端時
  5. 當您想要一個快速的應用開發周期,而現有的 .NET 開發人員幾乎不需要學習曲線時
  6. 當您想讓您的應用對搜索引擎友好時
  7. 當您希望應用程序在舊的瀏覽器上運行,而不依賴於 WebAssembly 時
  8. 當您想在 Visual Studio 中像調試普通 .NET 應用程序一樣調試 .NET 代碼時
  9. 當您想要構建內部網或低需求的面向公眾的應用程序時

何時不要使用 Blazor Server

  1. 當您的應用程序在高延遲環境中運行時
  2. 當您希望您的應用程序離線工作,而不需要一個與服務器的固定 SignalR 連接時
  3. 當您不想增加服務器資源以響應大量連接的 SignalR 客戶端時

Blazor WebAssembly App 是什麼?

這種託管模型是現代流行的 SPA 框架(比如 Angular、Vue 和 React)的直接競爭對手,也是大多數開發人員有興趣學習 Blazor 的主要原因。它允許開發人員使用 C# 取代 JavaScript 編寫所有前端 UI 邏輯。在這種託管模型中,第一次請求時,會將應用程序的 DLL 及其所有依賴項和小尺寸的 Mono .NET 運行時下載到客戶端。然後,客戶端中的 Mono 運行時就會加載並執行應用程序代碼。Blazor WebAssembly 程序可以用 C、C# 等其他語言編寫,然後編譯成 WebAssembly 位元組碼。

Blazor-WebAssembly-Apps

何時使用 Blazor WebAssembly

  1. 當您想要將整個應用程序編譯為靜態文件,並將它們提供給客戶端,而不需要服務器上的 .NET 運行時時。這意味着您的後端可以用 PHP、Node 或 Rails 編寫,並服務於用 Blazor 編寫的前端應用程序。
  2. 當您想要構建可以在客戶端脫機運行而無需持續連接到服務端的應用程序時。
  3. 當您想要將處理轉移到客戶端並減少服務端的負載時。
  4. 當您想在客戶端和服務端之間共享代碼和庫時。

何時不要使用 Blazor WebAssembly

  1. 當由於下載到客戶端的文件/DLL 太多,而您無法在有效負載上妥協時。
  2. 當您無法在緩慢的啟動時間上(特別是在網絡連接不佳的情況下)妥協時。
  3. 當您無法妥協於應用程序必須運行在具有完整的安全限制的瀏覽器沙箱環境中時。

為了更好地理解 Blazor 託管模型,讓我們在 Visual Studio 2019 中分別創建一個 Blazor Server 和 Blazor WebAssembly 應用程序。

在 Visual Studio 2019 中創建 Blazor Server 應用

打開 Visual Studio 2019 並點擊創建新項目。從可用的模板列表中選擇 Blazor App 模板並點擊下一步

Create-New-Blazor-App-Project-in-Visual-Studio-2019

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

Blazor-Server-App-in-Visual-Studio-2019

Visual Studio 將為我們創建一個 Blazor Server 應用程序,其中在解決方案資源管理器中包含以下文件夾和文件。

Blazor-Server-App-in-Solution-Explorer

讓我們來討論一下 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">

該文件還在末尾注入了 blazor.server.js 文件,此 JavaScript 文件包含設置 SignalR 連接到服務端的代碼。此連接在瀏覽器加載應用程序後立即建立,然後被用於服務端和客戶端瀏覽器之間的實時通信。如果您想了解有關 SignalR 的更多知識,請閱讀我的文章 Display Live Sports Updates using ASP.NET Core SignalR[2]

<script src="_framework/blazor.server.js"></script>

App.razor

這是 Blazor App 的主要組件,其主要工作是攔截路由並呈現 FoundNotFound 組件。 如果找到與路由匹配的組件,則呈現 Found 組件,如果未找到匹配的組件,則呈現 NotFound 組件。

<router appassembly="@typeof(Program).Assembly" preferexactmatches="@true">
    <found context="routeData">
        <routeview routedata="@routeData" defaultlayout="@typeof(MainLayout)">
    </routeview></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>
    </navmenu></div>
 
    <div class="main">
        <div class="top-row px-4">
            <a href="//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 連接進行的。

Default-Blazor-Server-App-Running-in-Browser

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

Blazor-Server-App-Files-in-Browser-Developer-Tools

在 Visual Studio 2019 中 創建 Blazor WebAssembly 應用

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

Create-Blazor-WebAssembly-App-in-Visual-Studio-2019

Visual Studio 將為我們創建一個 Blazor WebAssembly 應用程序,其中在解決方案資源管理器中包含以下文件夾和文件。

Blazor-Client-App-in-Solution-Explorer

您可以輕鬆發現這兩種類型的應用程序之間的一些差異。例如,在 Blazor WebAssembly 應用程序中沒有以下文件:

  1. _Host.cshtml
  2. Error.cshtml
  3. Startup.cs
  4. 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 頁面上使用計數器。 一切看起來感覺一模一樣,也沒有服務器端回傳。

Default-Blazor-Server-App-Running-in-Browser

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

Blazor-Client-App-Files-in-Browser-Developer-Tools

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

Blazor-Client-App-Files-in-Browser-Developer-Tools-Second-Request

總結

在本文中,我試圖為您介紹 Blazor SPA 框架的基本概念,我們看到了兩個 Blazor 應用程序使用兩種不同的託管模型進行託管。因為 Blazor 框架嚴重依賴於 razor 組件,所以兩個項目中的大部分代碼和文件都是相同的。這些組件是 Blazor 應用程序的構建塊,無論使用哪種託管模型,我們都可以以相似的方式構建這些組件。如果您喜歡本文,請分享它並傳播知識。

作者 : Waqas Anwar
翻譯 : 技術譯站
鏈接 : 英文原文


  1. //www.ezzylearning.net/tutorial/a-beginners-guide-to-blazor-server-and-webassembly-applications A Beginner』s Guide To Blazor Server and WebAssembly Applications ↩︎

  2. //www.ezzylearning.net/tutorial/display-live-sports-updates-using-asp-net-core-signalr Display Live Sports Updates using ASP.NET Core SignalR ↩︎