共用方式為


Blazor 應用程式的專案結構

提示

本內容節錄自《Blazor for ASP NET Web Forms Developers for Azure》電子書,可以從 .NET Docs 取得,也可以免費下載 PDF 離線閱讀。

Blazor-for-ASP-NET-Web-Forms-Developers 電子書封面縮圖。

儘管在專案結構上有顯著的差異,但 ASP.NET Web Forms 與 Blazor 共用許多類似的概念。 在這裡,我們將探討 Blazor 專案的結構,並與 ASP.NET Web Forms 專案的結構進行比較。

若要建立您的第一個 Blazor 應用程式,請遵循Blazor 使用者入門步驟中的指示進行。 您可以遵循指示來建立 Blazor Server 應用程式或裝載於 ASP.NET Core 中的 BlazorWebAssembly 應用程式。 除了裝載模型特定的邏輯之外,這兩個專案中的大部分程式碼都相同。

專案檔

Blazor Server 應用程式是 .NET 專案。 Blazor Server 應用程式的專案檔會盡可能簡單:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

</Project>

BlazorWebAssembly 應用程式的專案檔看起來稍微更複雜 (確切版本號碼可能會有所不同):

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
  </ItemGroup>

</Project>

BlazorWebAssembly 專案是以 Microsoft.NET.Sdk.BlazorWebAssembly (而不是 Microsoft.NET.Sdk.Web SDK) 為目標,因為這些專案會在瀏覽器中以 WebAssembly 型 .NET runtime 為基礎執行。 您無法像在伺服器或開發人員電腦上一樣,將 .NET 安裝到網頁瀏覽器中。 因此,專案會使用個別套件參考來參考 Blazor 架構。

相較之下,預設 ASP.NET Web Forms 專案在其 .csproj 檔案中包含近 300 行的 XML,其中大部分會明確列出專案中的各種程式碼和內容檔案。 自 .NET 5 版本起,Blazor Server 和 BlazorWebAssembly 應用程式可以輕鬆地共用一個整合執行階段。

個別組件參考雖然受到支援,但在 .NET 專案中較不常見。 大部分的專案相依性都會當做 NuGet 套件參考來處理。 您只需要參考 .NET 專案中的最上層套件相依性。 系統會自動包含可轉移的相依性。 您可以使用 <PackageReference> 元素將套件參考新增至專案檔,而不是使用 ASP.NET Web Forms 專案中常見的 packages.config 檔案來參考套件。

<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>

進入點

Blazor Server 應用程式的進入點是在 Program.cs 檔案中定義,如主控台應用程式中所示。 當應用程式執行時,會使用 Web 應用程式特定的預設值來建立和執行 Web 主機執行個體。 Web 主機會管理 Blazor Server 應用程式的生命週期,並設定主機層級服務。 這類服務的範例包括組態、記錄、相依性插入和 HTTP 伺服器。 下列程式碼大部分會重複使用,而且通常會保持不變。

using BlazorApp3.Areas.Identity;
using BlazorApp3.Data;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

BlazorWebAssembly 應用程式也會在 Program.cs 中定義進入點。 程式碼看起來稍有不同。 程式碼的相似之處在於會設定應用程式主機,將相同的主機層級服務提供給應用程式。 不過,WebAssembly 應用程式主機不會設定 HTTP 伺服器,因為它會直接在瀏覽器中執行。

Blazor 應用程式不會使用 Global.asax 檔案來定義應用程式的啟動邏輯。 相反地,此邏輯會包含在 Program.cs 或從 Program.cs 參考的相關 Startup 類別中。 不論是哪一種方式,此程式碼都會用來設定應用程式和任何應用程式特定的服務。

在 Blazor Server 應用程式中,顯示的 Program.cs 檔案會用來設定用戶端瀏覽器與伺服器間 Blazor 所使用即時連線的端點。

在 BlazorWebAssembly 應用程式中,Program.cs 檔案會定義應用程式的根元件及其應該轉譯的位置:

using BlazorApp1;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();

靜態檔案

不同於 ASP.NET Web Forms 專案,Blazor 專案中並非所有檔案都能要求作為靜態檔案。 只有 wwwroot 資料夾中的檔案是 Web 可定址。 此資料夾稱為應用程式的「Web 根目錄」。 應用程式 Web 根目錄之外的任何檔案都「不是」Web 可定址。 此設定提供額外的安全性層級,以防止意外在網路上暴露專案檔。

組態

ASP.NET Web Forms 應用程式中的組態通常會使用一或多個 web.config 檔案來處理。 Blazor 應用程式通常沒有 web.config 檔案。 如果有,這些檔案只會用來設定裝載於 IIS 時的 IIS 特定設定。 相反地,Blazor Server 應用程式使用 ASP.NET Core 組態抽象概念。 (BlazorWebAssembly 應用程式目前不支援相同的組態抽象概念,但這可能是未來新增的功能。)例如,預設 Blazor Server 應用程式會將某些設定儲存在 appsettings.json 中。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

您將在組態一節中深入了解 ASP.NET Core 專案中的組態。

Razor 元件

Blazor 專案中的大部分檔案都是 .razor 檔案。 Razor 是以 HTML 和 C# 為基礎的範本化語言,可用來動態產生 Web UI。 .razor 檔案會定義組成應用程式 UI 的元件。 Blazor Server 和 BlazorWebAssembly 應用程式的大部分元件都相同。 Blazor 中的元件類似於 ASP.NET Web Forms 中的使用者控制項。

建置專案時,每個 Razor 元件檔案都會編譯成 .NET 類別。 產生的類別會擷取元件的狀態、轉譯邏輯、生命週期方法、事件處理常式和其他邏輯。 您將在使用 Blazor 建置可重複使用的 UI 元件一節中深入了解如何撰寫元件。

_Imports.razor 檔案不是 Razor 元件檔案。 相反地,這些檔案會定義一組 Razor 指示詞,以匯入至相同資料夾及其子資料夾中的其他 .razor 檔案。 例如,_Imports.razor 檔案是為常用的命名空間新增 using 指示詞的傳統方式:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using BlazorApp1
@using BlazorApp1.Shared

頁面

Blazor 應用程式中的頁面位於何處? Blazor 不會定義可定址頁面的個別副檔名 (例如 ASP.NET Web Forms 應用程式中的 .aspx 檔案), 而是藉由將路由指派給元件來定義頁面。 路由通常會使用 @page Razor 指示詞來指派。 例如,在 Pages/Counter.razor 檔案中撰寫的 Counter 元件會定義下列路由:

@page "/counter"

Blazor 中的路由會在用戶端處理,而不是在伺服器上。 當使用者在瀏覽器中瀏覽時,Blazor 會攔截瀏覽,然後使用相符的路由轉譯元件。

目前無法像使用 .aspx 頁面或 ASP.NET Core Razor Pages 一樣,透過元件的檔案位置來推斷元件路由。 未來可能會新增這項功能。 您必須在元件上明確指定每個路由。 將可路由的元件儲存在 Pages 資料夾中沒有特殊意義,只是慣例。

您將在頁面、路由和版面配置一節中深入了解 Blazor 中的路由。

版面配置

在 ASP.NET Web Forms 應用程式中,常見的頁面配置是使用主版頁面 (Site.Master) 來處理。 在 Blazor 應用程式中,頁面配置是使用版面配置元件 (Shared/MainLayout.razor) 來處理。 頁面、路由和版面配置一節中將更詳細討論版面配置元件。

啟動載入 Blazor

若要啟動載入 Blazor,應用程式必須:

  • 指定應該在頁面上轉譯根元件 (App.Razor) 的位置。
  • 新增對應的 Blazor 架構指令碼。

在 Blazor Server 應用程式中,根元件的主機頁面是在 _Host.cshtml 檔案中定義。 此檔案會定義 Razor 頁面,而不是元件。 Razor Pages 使用 Razor 語法來定義可定址的伺服器頁面,這與 .aspx 頁面非常類似。

@page "/"
@namespace BlazorApp3.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = "_Layout";
}

<component type="typeof(App)" render-mode="ServerPrerendered" />

render-mode 屬性會用來定義應該轉譯根層級元件的位置。 RenderMode 選項會指出應該如何轉譯元件。 下表概述支援的 RenderMode 選項。

選項 描述
RenderMode.Server 建立與瀏覽器的連線之後,以互動方式轉譯
RenderMode.ServerPrerendered 預先轉譯,再以互動方式轉譯
RenderMode.Static 轉譯為靜態內容

_Layout.cshtml 檔案包含應用程式及其元件的預設 HTML。

@using Microsoft.AspNetCore.Components.Web
@namespace BlazorApp3.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="BlazorApp3.styles.css" rel="stylesheet" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

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

指令碼參考 _framework/blazor.server.js 建立與伺服器的即時連接,然後處理所有使用者互動和 UI 更新。

在 BlazorWebAssembly 應用程式中,主機頁面是 wwwroot/index.html 底下的簡單靜態 HTML 檔案。 具有名為 app 之識別碼的 <div> 元素會用來指出應該轉譯根元件的位置。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorApp1</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorApp1.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

要轉譯的根元件會在應用程式的 Program.cs 檔案中指定,以彈性地透過相依性插入來註冊服務。 如需詳細資訊,請參閱 ASP.NET Core Blazor 相依性插入

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

建置輸出

建置 Blazor 專案時,所有 Razor 元件和程式碼檔案都會編譯成單一組件。 不同於 ASP.NET Web Forms 專案,Blazor 不支援 UI 邏輯的執行階段編譯。

使用熱重新載入執行應用程式

若要執行 Blazor Server 應用程式,請在 Visual Studio 中按 F5 執行並附加偵錯工具,或按 Ctrl + F5 執行而不附加偵錯工具。

若要執行 BlazorWebAssembly 應用程式,請選擇下列其中一種方法:

  • 使用開發伺服器直接執行用戶端專案。
  • 使用 ASP.NET Core 裝載應用程式時,執行伺服器專案。

BlazorWebAssembly 應用程式可以在瀏覽器和 Visual Studio 中進行偵錯。 如需詳細資料,請參閱偵錯 ASP.NET Core BlazorWebAssembly

Blazor Server 和 BlazorWebAssembly 應用程式都支援 Visual Studio 中的熱重新載入。 熱重新載入是一項功能,可在瀏覽器中即時自動更新對 Blazor 應用程式所做的變更。 您可以透過工具列中的熱重新載入圖示,切換是否啟用熱重新載入:

Visual Studio 2022:熱重新載入功能表項目和圖示。

選取圖示旁的插入號會顯示其他選項。 您可以開啟或關閉熱重新載入、重新啟動應用程式,以及切換是否應該在儲存檔案時進行熱重新載入。

Visual Studio 2022:具有擴充選項的熱重新載入功能表項目。

您也可以存取其他組態選項。 組態對話方塊可讓您指定應該在偵錯時 (此時也會啟用 [編輯後繼續])、啟動但不偵錯時或儲存檔案時啟用熱重新載入。

Visual Studio 2022:「工具 > 選項 > 偵錯 > .NET/C++ 熱重新載入」對話方塊中的熱重新載入設定選項。

熱重新載入大幅簡化了「開發人員內部迴圈」。 如果沒有熱重新載入,Blazor 開發人員通常需要在每次變更之後重新啟動和重新執行應用程式,並視需要瀏覽至應用程式的適當部分。 有了熱重新載入,在大多數情況下,不需要重新啟動就能對執行中的應用程式進行變更。 熱重新載入甚至會保留頁面的狀態,因此不需要重新輸入表單值,或讓應用程式回到所需的位置。