ASP.NET Core Razor 元件

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

本文說明如何在 Blazor 應用程式中建立和使用 Razor 元件,包括 Razor 語法、元件命名、命名空間和元件參數的指引。

Razor 元件

Blazor 應用程式會使用 Razor 元件來建置 (非正式的名稱為 Blazor 元件,或僅稱 元件)。 元件是使用者介面 (UI) 的獨立部分,具有處理邏輯而能夠啟用動態行為。 元件可巢狀處理、重複使用、在專案之間共用,以及用於 MVC 和 Razor Pages 應用程式。

元件會轉譯為瀏覽器文件物件模型 (DOM) 的記憶體內部表示法 (稱為「轉譯樹」),可用來以彈性且有效率的方式更新 UI。

雖然「Razor 元件」與其他 ASP.NET Core 內容轉譯技術共用一些命名,但 Razor 元件必須與 ASP.NET Core 中的下列不同功能區別:

  • Razor 檢視,這是 MVC 應用程式的 Razor 型標記頁面。
  • 檢視元件,用於轉譯內容區塊,而不是 Razor Pages 和 MVC 應用程式中的整體回應。

元件類別

元件會使用 C# 和 HTML 標記的組合,實作到副檔名為 .razorRazor 元件檔案中。

根據預設,ComponentBase 是 Razor 元件檔案所描述元件的基底類別。 ComponentBase 會實作元件的最低抽象概念,即 IComponent 介面。 ComponentBase 會定義基本功能的元件屬性和方法,例如,處理一組內建元件生命週期事件。

ComponentBasedotnet/aspnetcore 參考來源中:參考來源包含內建生命週期事件的其他備註。 不過,請記住,元件功能的內部實作可能隨時有所變更,恕不另行通知。

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

開發人員通常會從 Razor 元件檔案 (.razor) 建立 Razor 元件,或以 ComponentBase 做為其元件的基礎,但也可以實作 IComponent 來建置元件。 實作 IComponent 的開發人員建置元件,可以透過開發人員必須建立及維護的事件和生命週期方法手動觸發轉譯,而不需要對轉譯採取低階控制。

Razor 語法

元件會使用 Razor 語法。 元件廣泛使用兩個 Razor 功能,分別是「指示詞」和「指示詞屬性」。 這兩個功能是會出現在 Razor 標記中、且前面加上 @ 的保留關鍵字:

  • 指示詞:會變更元件標記的剖析或運作方式。 例如,@page 指示詞會指定具有路由範本的可路由元件,而且使用者只要透過瀏覽器在特定 URL 中提出要求即可直接連線。

    依照慣例,元件定義 (.razor 檔案) 頂端的元件指示詞會以一致的順序放置。 重複的指示詞會依據命名空間或類型按字母順序放置,但有特殊第二層排序的 @using 指示詞除外。

    Blazor 範例應用程式和文件採用下列順序。 Blazor 專案範本所提供的元件可能會與下列順序不同,並使用不同的格式。 例如,Blazor 架構 Identity 元件在 @using 指示詞區塊與 @inject 指示詞區塊之間包含空白行。 您可以在自己的應用程式中自由使用自訂排序配置和格式。

    文件和範例應用程式 Razor 指示詞順序:

    • @page
    • @rendermode (.NET 8 或更新版本)
    • @using
      • System 命名空間 (字母順序)
      • Microsoft 命名空間 (字母順序)
      • 第三方 API 命名空間 (字母順序)
      • 應用程式命名空間 (字母順序)
    • 其他指示詞 (字母順序)

    指示詞間不會出現空白行。 指示詞與 Razor 標記的第一行之間會出現一個空白行。

    範例:

    @page "/doctor-who-episodes/{season:int}"
    @rendermode InteractiveWebAssembly
    @using System.Globalization
    @using System.Text.Json
    @using Microsoft.AspNetCore.Localization
    @using Mandrill
    @using BlazorSample.Components.Layout
    @attribute [Authorize]
    @implements IAsyncDisposable
    @inject IJSRuntime JS
    @inject ILogger<DoctorWhoEpisodes> Logger
    
    <PageTitle>Doctor Who Episode List</PageTitle>
    
    ...
    
  • 指示詞屬性:會變更元件元素的剖析或運作方式。

    範例:

    <input @bind="episodeId" />
    

    您可針對非明確的 Razor 運算式 (@bind="@episodeId"),在指示詞屬性值的前面加入 at 符號 (@),但我們不建議這麼做,而且文件不會採用範例中的方法。

本文和其他 Blazor 元件集文章會進一步說明元件中使用的指示詞和指示詞屬性。 如需 Razor 語法的一般資訊,請參閱 ASP.NET Core 的 Razor 語法參考

元件名稱、類別名稱和命名空間

元件的名稱必須以大寫字元開頭:

支援:ProductDetail.razor

不支援:productDetail.razor

Blazor 文件中使用的通用 Blazor 命名慣例包括:

  • 檔案路徑和檔案名稱會使用 Pascal 大小寫†並顯示在顯示程式碼範例之前。 如果路徑存在,則代表一般資料夾位置。 例如,Components/Pages/ProductDetail.razor 代表 ProductDetail 元件的檔案名稱為 ProductDetail.razor,且位於應用程式的 Components 資料夾的 Pages 資料夾中。
  • 可路由元件的元件檔案路徑會符合其 URL (使用烤肉串式大小寫‡),且元件路由範本中單字之間的空格會由連字號代替。 例如,針對路由範本為 /product-detail (@page "/product-detail") 的 ProductDetail 元件,我們會透過瀏覽器在相對 URL /product-detail 中提出要求。

†Pascal 命名法 (大駝峰式命名法) 是不含空格和標點符號的命名慣例,且每個單字的第一個字母會大寫,包括第一個單字。
‡烤肉串式大小寫是不含空格和標點符號的命名慣例,並且在字組之間會使用小寫字母和虛線。

元件是一般的 C# 類別,而且可以放到專案內的任何位置。 產生網頁的元件通常位於 Components/Pages 資料夾中。 非網頁元件則經常放在 Components 資料夾中,或放在新增至專案的自訂資料夾中。

一般而言,元件的命名空間會衍生自應用程式的根命名空間,以及元件在應用程式內的位置 (資料夾)。 如果應用程式的根命名空間是 BlazorSample,而且 Counter 元件位於 Components/Pages 資料夾中:

  • Counter 元件的命名空間為 BlazorSample.Components.Pages
  • 元件的完整類型名稱為 BlazorSample.Components.Pages.Counter

對於保有元件的自訂資料夾,請將 @using 指示詞新增至父元件或新增至應用程式的 _Imports.razor 檔案。 下列範例會讓 AdminComponents 資料夾中的元件變為可用狀態:

@using BlazorSample.AdminComponents

注意

_Imports.razor 檔案中的 @using 指示詞只會套用至 Razor 檔案 (.razor),而不會套用至 C# 檔案 (.cs)。

支援別名 using 陳述式。 在下列範例中,GridRendering 元件的公用 WeatherForecast 類別會以應用程式其他位置元件中的 WeatherForecast 形式提供:

@using WeatherForecast = Components.Pages.GridRendering.WeatherForecast

您也可以使用元件的完整名稱來參考元件,如此便不必使用 @using 指示詞。 下列範例會直接參考應用程式 AdminComponents/Pages 資料夾中的 ProductDetail 元件:

<BlazorSample.AdminComponents.Pages.ProductDetail />

使用 Razor 撰寫的元件,其命名空間會以下列項目作為基礎 (依優先順序列出):

  • Razor 檔案標記中的 @namespace 指示詞 (例如,@namespace BlazorSample.CustomNamespace)。
  • 專案在專案檔案中的 RootNamespace (例如,<RootNamespace>BlazorSample</RootNamespace>)。
  • 專案命名空間以及從專案根目錄到元件的路徑。 例如,架構會將專案命名空間為 BlazorSample{PROJECT NAMESPACE}/Components/Pages/Home.razor 解析為 Home 元件的命名空間 BlazorSample.Components.Pages{PROJECT NAMESPACE} 是專案命名空間。 元件會遵循 C# 名稱繫結規則。 在此範例的 Home 元件中,範圍內的元件是所有元件:
    • 在相同資料夾中是 Components/Pages
    • 專案根目錄中未明確指定不同命名空間的元件。

支援下列項目:

  • global:: 限定性條件。
  • 部分限定名稱。 例如,您無法將 @using BlazorSample.Components 新增至元件,然後使用 <Layout.NavMenu></Layout.NavMenu> 參考應用程式 Components/Layout 資料夾 (Components/Layout/NavMenu.razor) 中的 NavMenu 元件。

元件的名稱必須以大寫字元開頭:

支援:ProductDetail.razor

不支援:productDetail.razor

Blazor 文件中使用的通用 Blazor 命名慣例包括:

  • 檔案路徑和檔案名稱會使用 Pascal 大小寫†並顯示在顯示程式碼範例之前。 如果路徑存在,則代表一般資料夾位置。 例如,Pages/ProductDetail.razor 代表 ProductDetail 元件的檔案名稱為 ProductDetail.razor,且位於應用程式的 Pages 資料夾中。
  • 可路由元件的元件檔案路徑會符合其 URL (使用烤肉串式大小寫‡),且元件路由範本中單字之間的空格會由連字號代替。 例如,針對路由範本為 /product-detail (@page "/product-detail") 的 ProductDetail 元件,我們會透過瀏覽器在相對 URL /product-detail 中提出要求。

†Pascal 命名法 (大駝峰式命名法) 是不含空格和標點符號的命名慣例,且每個單字的第一個字母會大寫,包括第一個單字。
‡烤肉串式大小寫是不含空格和標點符號的命名慣例,並且在字組之間會使用小寫字母和虛線。

元件是一般的 C# 類別,而且可以放到專案內的任何位置。 產生網頁的元件通常位於 Pages 資料夾中。 非網頁元件則經常放在 Shared 資料夾中,或放在新增至專案的自訂資料夾中。

一般而言,元件的命名空間會衍生自應用程式的根命名空間,以及元件在應用程式內的位置 (資料夾)。 如果應用程式的根命名空間是 BlazorSample,而且 Counter 元件位於 Pages 資料夾中:

  • Counter 元件的命名空間為 BlazorSample.Pages
  • 元件的完整類型名稱為 BlazorSample.Pages.Counter

對於保有元件的自訂資料夾,請將 @using 指示詞新增至父元件或新增至應用程式的 _Imports.razor 檔案。 下列範例會讓 AdminComponents 資料夾中的元件變為可用狀態:

@using BlazorSample.AdminComponents

注意

_Imports.razor 檔案中的 @using 指示詞只會套用至 Razor 檔案 (.razor),而不會套用至 C# 檔案 (.cs)。

支援別名 using 陳述式。 在下列範例中,GridRendering 元件的公用 WeatherForecast 類別會以應用程式其他位置元件中的 WeatherForecast 形式提供:

@using WeatherForecast = Pages.GridRendering.WeatherForecast

您也可以使用元件的完整名稱來參考元件,如此便不必使用 @using 指示詞。 下列範例會直接參考應用程式 Components 資料夾中的 ProductDetail 元件:

<BlazorSample.Components.ProductDetail />

使用 Razor 撰寫的元件,其命名空間會以下列項目作為基礎 (依優先順序列出):

  • Razor 檔案標記中的 @namespace 指示詞 (例如,@namespace BlazorSample.CustomNamespace)。
  • 專案在專案檔案中的 RootNamespace (例如,<RootNamespace>BlazorSample</RootNamespace>)。
  • 專案命名空間以及從專案根目錄到元件的路徑。 例如,架構會將專案命名空間為 BlazorSample{PROJECT NAMESPACE}/Pages/Index.razor 解析為 Index 元件的命名空間 BlazorSample.Pages{PROJECT NAMESPACE} 是專案命名空間。 元件會遵循 C# 名稱繫結規則。 在此範例的 Index 元件中,範圍內的元件是所有元件:
    • 在相同資料夾中是 Pages
    • 專案根目錄中未明確指定不同命名空間的元件。

支援下列項目:

  • global:: 限定性條件。
  • 部分限定名稱。 例如,您無法將 @using BlazorSample 新增至元件,然後使用 <Shared.NavMenu></Shared.NavMenu> 參考應用程式 Shared 資料夾 (Shared/NavMenu.razor) 中的 NavMenu 元件。

部分類別支援

元件會以 C# 部分類別的形式產生,並使用下列任一方法來撰寫:

  • 單一檔案包含一或多個 @code 區塊、HTML 標記和 Razor 標記中所定義的 C# 程式碼。 Blazor 專案範本會使用此單一檔案方法來定義其元件。
  • HTML 和 Razor 標記會放在 Razor 檔案 (.razor) 中。 C# 程式碼會放在定義為部分類別的程式碼後置檔案 (.cs) 中。

注意

定義元件特定樣式的元件樣式表是不同的檔案 (.css)。 BlazorCSS 隔離稍後會在 ASP.NET CORE Blazor CSS 隔離中說明。

下列範例會顯示從 Blazor 專案範本產生的應用程式中,所具有的預設 Counter 元件與 @code 區塊。 標記和 C# 程式碼位於相同檔案中。 這是在撰寫元件時最常採用的方法。

Counter.razor

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

下列 Counter 元件會使用具有部分類別的程式碼後置檔案,從 C# 程式碼中分割展示 HTML 和 Razor 標記。 一些組織和開發人員偏好將標記與 C# 程式碼分割,以組織其元件程式碼,符合他們偏好的運作方式。 例如,組織的 UI 專家可以獨立處理元件 C# 邏輯的另一位開發人員,並獨立處理展示層。 使用自動產生的程式碼或來源產生器時,此方法也很有用。 如需詳細資訊,請參閱部分類別和方法 (C# 程式設計手冊)

CounterPartialClass.razor

@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

CounterPartialClass.razor.cs

namespace BlazorSample.Components.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

        private void IncrementCount()
        {
            currentCount++;
        }
    }
}

_Imports.razor 檔案中的 @using 指示詞只會套用至 Razor 檔案 (.razor),而不會套用至 C# 檔案 (.cs)。 請視需要將命名空間新增至部分類別檔案。

元件所使用的典型命名空間:

using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Sections
using Microsoft.AspNetCore.Components.Web;
using static Microsoft.AspNetCore.Components.Web.RenderMode;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

典型命名空間也包含應用程式的命名空間,以及對應至應用程式 Components 資料夾的命名空間:

using BlazorSample;
using BlazorSample.Components;

也可以包含其他資料夾,例如 Layout 資料夾:

using BlazorSample.Components.Layout;
using System.Net.Http;
using System.Net.Http.Json;
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;

典型命名空間也包含應用程式的命名空間,以及對應至應用程式 Shared 資料夾的命名空間:

using BlazorSample;
using BlazorSample.Shared;
using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

典型命名空間也包含應用程式的命名空間,以及對應至應用程式 Shared 資料夾的命名空間:

using BlazorSample;
using BlazorSample.Shared;

指定基底類別

@inherits 指示詞可用來指定元件的基底類別。 不同於使用只從 C# 邏輯分割標記的部分類別,使用基底類別可讓您繼承 C# 程式碼,以便跨共用基底類別屬性和方法的元件群組使用。 使用基底類別可減少應用程式中的程式碼備援,且在將基底類別程式碼從類別庫提供給多個應用程式時很有用。 如需詳細資訊,請參閱在 C# 和 .NET 中繼承

在下列範例中,BlazorRocksBase1 基底類別衍生自 ComponentBase

BlazorRocks1.razor

@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>

BlazorRocksBase1.cs

using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}

路由

在 Blazor 中實現路由功能的方法是,使用 @page 指示詞向應用程式中的每個可存取元件提供路由範本。 在編譯具有 @page 指示詞的 Razor 檔案時,所產生的類別會獲得 RouteAttribute 以指定路由範本。 在執行階段,路由器會搜尋具有 RouteAttribute 的元件類別,並轉譯其路由範本符合所要求 URL 的任何元件。

下列 HelloWorld 元件會使用 /hello-world 的路由範本,且元件的轉譯網頁會到達相對 URL /hello-world

HelloWorld.razor

@page "/hello-world"

<PageTitle>Hello World!</PageTitle>

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>

不論您是否有將元件新增至應用程式的 UI 導覽,上述元件都會透過瀏覽器在 /hello-world 中載入。 您也可以選擇將元件新增至 NavMenu 元件,讓後者的連結出現在應用程式的 UI 型導覽中。

針對上述 HelloWorld 元件,您可以將 NavLink 元件新增至 NavMenu 元件。 如需詳細資訊,包括 NavLinkNavMenu 元件的描述,請參閱 ASP.NET Core Blazor 路由和導覽

加成

元件的 UI 會使用 Razor 語法來定義,此語法由 Razor 標記、C# 和 HTML 組成。 在編譯應用程式時,HTML 標記和 C# 轉譯邏輯會轉換為元件類別。 所產生的類別名稱與檔案的名稱相符。

元件類別的成員會定義於一或多個 @code 區塊中。 在 @code 區塊中,系統會使用 C# 來指定和處理元件狀態:

  • 屬性和欄位的初始設定式。
  • 父元件和路由參數所傳遞引數中的參數值。
  • 使用者事件處理、生命週期事件和自訂元件邏輯的方法。

使用開頭為 @ 符號的 C# 運算式即可在轉譯邏輯中使用元件成員。 例如,藉由在欄位名稱前面加上 @ 即可轉譯 C# 欄位。 下列 Markup 元件會評估和轉譯:

  • headingFontStyle,以取得標題元素的 CSS 屬性值 font-style
  • headingText,以取得標題元素的內容。

Markup.razor

@page "/markup"

<PageTitle>Markup</PageTitle>

<h1>Markup Example</h1>

<h2 style="font-style:@headingFontStyle">@headingText</h2>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

注意

整個 Blazor 文件的範例會指定私人成員的 private 存取修飾詞。 私人成員的範圍僅限元件的類別。 不過,如果沒有任何存取修飾詞存在,C# 則會採用 private 存取修飾詞,因此您可以選擇是否要在自己的程式碼中將成員明確標記為「private」。 如需存取修飾詞的詳細資訊,請參閱 存取修飾詞 (C# 程式設計指南)

Blazor 架構會在內部以轉譯樹狀結構 (由元件的 DOM 和階層式樣式表物件模型 (CSSOM) 組合而成) 的形式處理元件。 初次轉譯元件之後,便會重新產生元件的轉譯樹狀結構以回應事件。 Blazor 會拿新的轉譯樹狀結構與上一個轉譯樹狀結構做比較,並將任何修改套用至瀏覽器的 DOM 以供顯示。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件轉譯

C# 控制項結構、指示詞和指示詞屬性的 Razor 語法採用小寫 (範例:@if@code@bind)。 屬性名稱則會大寫 (範例:例如 LayoutComponentBase.Body@Body)。

非同步方法 (async) 不支援傳回 void

Blazor 架構不會追蹤 void 傳回的非同步方法 (async)。 因此,如果傳回 void,則不會攔截到例外狀況。 請一律從非同步方法傳回 Task

巢狀元件

元件可以透過使用 HTML 語法來宣告元件,以包含其他元件。 使用元件的標記看起來像是 HTML 標籤,其中標籤名稱是元件類型。

請看一下下列 Heading 元件,其可供其他元件使用以顯示標題。

Heading.razor

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

HeadingExample 元件中的下列標記會在 <Heading /> 標籤出現的位置轉譯上述 Heading 元件。

HeadingExample.razor

@page "/heading-example"

<PageTitle>Heading</PageTitle>

<h1>Heading Example</h1>

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />

如果元件包含的 HTML 元素,其大寫的第一個字母與相同命名空間內的元件名稱不符,就會發出警告,指出該元素的名稱不符預期。 為元件的命名空間新增 @using 指示詞會讓元件變為可用狀態,如此便能解決警告。 如需詳細資訊,請參閱元件名稱、類別名稱和命名空間一節。

本節所示的 Heading 元件範例沒有 @page 指示詞,因此使用者無法透過在瀏覽器中直接要求來直接存取 Heading 元件。 不過,任何具有 @page 指示詞的元件都可以巢狀化到另一個元件中。 如果透過在 Razor 檔案頂端包含 @page "/heading" 而能夠直接存取 Heading 元件,則位於 /heading/heading-example 的瀏覽器要求都會轉譯元件。

元件參數

「元件參數」會將資料傳遞至元件,其定義方式則是在元件類別上使用公用的 C# 屬性[Parameter] 屬性。 在下列範例中,內建的參考類型 (System.String) 和使用者定義的參考類型 (PanelBody) 會以元件參數的形式來傳遞。

PanelBody.cs

namespace BlazorSample;

public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}
public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

ParameterChild.razor

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new PanelBody()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

警告

支援為元件參數提供初始值,但請勿在第一次轉譯元件之後,建立會寫入到其自有參數的元件。 如需詳細資訊,請參閱避免在 ASP.NET Core 中覆寫參數Blazor

ParameterChild 元件的 TitleBody 元件參數會由轉譯元件執行個體的 HTML 標籤中的引數來設定。 下列 ParameterParent 元件會轉譯兩個 ParameterChild 元件:

  • 第一個 ParameterChild 元件在轉譯時不會提供參數引數。
  • 第二個 ParameterChild 元件會接收 ParameterParent 元件中的 TitleBody 值,其會使用明確的 C# 運算式來設定 PanelBody 屬性的值。

Parameter1.razor

@page "/parameter-1"

<PageTitle>Parameter 1</PageTitle>

<h1>Parameter Example 1</h1>

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent 元件未提供元件參數值時,下列從 ParameterParent 元件轉譯而來的 HTML 標記會顯示 ParameterChild 元件的預設值。 當 ParameterParent 元件提供元件參數值時,這些值便會取代 ParameterChild 元件的預設值。

注意

為了清楚起見,下列轉譯的 HTML 標記中未顯示轉譯的 CSS 樣式類別。

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

將方法的 C# 欄位、屬性或結果指派給元件參數來做為 HTML 屬性值。 屬性的值通常可以是符合參數類型的任何 C# 運算式。 屬性的值可以選擇性地以 Razor 保留 @ 符號前置,但並非必要。

如果元件參數的類型為字串,則屬性值預設會改以視為 C# 字串常值。 如果您想要改為指定 C# 運算式,請使用 @ 前置詞。

下列 ParameterParent2 元件會顯示上述 ParameterChild 元件的四個執行個體,並將其 Title 參數值設定為:

  • title 欄位的值。
  • GetTitle C# 方法的結果。
  • 使用 ToLongDateString 並以完整格式表示的當前本機日期,其會使用隱含的 C# 運算式
  • panelData 物件的 Title 屬性。

根據 HTML5 的規格,在大部分情況下,您可以選擇是否在參數屬性值外加上引號。 例如,Value=this 會受到支援,Value="this" 則不會。 不過,我們還是建議使用引號,因為這麼做不僅方便記住,也廣為 Web 型技術所採用。

在整個文件中,程式碼範例:

  • 一律使用引號。 範例: Value="this".
  • 非必要請勿對非常值使用 @ 前置詞。 範例:Count="ct",其中 ct 是數字類型的變數。 Count="@ct" 是有效的文體方法,但文件和範例不會採用慣例。
  • Razor 運算式外部的常值請一律避免 @。 範例: IsFixed="true". 這包括關鍵字 (例如 this) 和 null,但您可以視需要選擇加以使用。 例如,IsFixed="@true" 雖不常見,但仍受到支援。

Parameter2.razor

@page "/parameter-2"

<PageTitle>Parameter 2</PageTitle>

<h1>Parameter Example 2</h1>

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new PanelData();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

注意

將 C# 成員指派給元件參數時,請勿在參數的 HTML 屬性前面加上 @

正確 (Title 是字串參數,Count 是數字類型參數):

<ParameterChild Title="@title" Count="ct" />
<ParameterChild Title="@title" Count="@ct" />

不正確:

<ParameterChild @Title="@title" @Count="ct" />
<ParameterChild @Title="@title" @Count="@ct" />

和在 Razor 頁面 (.cshtml) 中不同,Blazor 無法在轉譯元件時於 Razor 運算式中執行非同步工作。 這是因為 Blazor 的設計目的是轉譯互動式 UI。 在互動式 UI 中,畫面一定要顯示內容,因此封鎖轉譯流程並不合理。 相反地,非同步工作會在其中一個非同步生命週期事件期間執行。 在每個非同步生命週期事件過後,元件可能會重新轉譯。 下列 Razor 語法受支援:

<ParameterChild Title="@await ..." />

上述範例中的程式碼會在建置應用程式時產生「編譯器錯誤」

「await」運算子只能用於非同步方法。 請考慮使用「async」修飾詞標記這個方法,並將其傳回類型變更為「Task」。

若要以非同步方式取得上述範例中 Title 參數的值,元件可以使用 OnInitializedAsync 生命週期事件,如下列範例所示:

<ParameterChild Title="@title" />

@code {
    private string? title;
    
    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

如需詳細資訊,請參閱 ASP.NET Core Razor 元件生命週期

支援使用明確的 Razor 運算式來串連文字與運算式結果以指派給參數。 下列範例會試圖將文字「Set by 」與物件的屬性值串連起來。 雖然 Razor 頁面 (.cshtml) 支援此語法,但若要指派給子系在元件中的 Title 參數則會無效。 下列 Razor 語法受支援:

<ParameterChild Title="Set by @(panelData.Title)" />

上述範例中的程式碼會在建置應用程式時產生「編譯器錯誤」

元件屬性 (attribute) 不支援複雜內容 (混合 C# 和標記)。

若要支援撰寫值的指派,請使用方法、欄位或屬性 (property)。 下列範例會在 C# 方法 GetTitle 中執行「Set by 」和物件屬性 (property) 值的串連:

Parameter3.razor

@page "/parameter-3"

<PageTitle>Parameter 3</PageTitle>

<h1>Parameter Example 3</h1>

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new PanelData();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

如需詳細資訊,請參閱 ASP.NET Core 的 Razor 語法參考

警告

支援為元件參數提供初始值,但請勿在第一次轉譯元件之後,建立會寫入到其自有參數的元件。 如需詳細資訊,請參閱避免在 ASP.NET Core 中覆寫參數Blazor

元件參數應該宣告為自動屬性,這表示其不應該在其 getset 存取子中包含自訂邏輯。 例如,下列 StartData 屬性是自動屬性:

[Parameter]
public DateTime StartData { get; set; }

請勿將自訂邏輯放在 getset 存取子中,因為元件參數純粹是作為父元件用來將資訊流向子元件的通道而已。 如果子元件屬性的 set 存取子包含的邏輯會導致父元件重新轉譯,則會產生無限循環的轉譯。

若要轉換所接收的參數值:

  • 讓參數屬性仍作為自動屬性,以代表所提供的未經處理資料。
  • 建立不同的屬性或方法,以根據參數屬性提供經過轉換的資料。

覆寫 OnParametersSetAsync 以在每次收到新資料時轉換所接收的參數。

支援在元件參數中寫入初始值,因為指派初始值並不會干擾 Blazor 自動轉譯元件。 在元件中,將目前的本機 DateTimeDateTime.Now 指派給 StartData 屬於有效的語法:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

在初始指派 DateTime.Now 之後,請勿在開發人員程式碼的 StartData 中指派值。 如需詳細資訊,請參閱避免在 ASP.NET Core 中覆寫參數Blazor

套用 [EditorRequired] 屬性以指定必要的元件參數。 如果未提供參數值,編輯器或建置工具可能會向使用者顯示警告。 這個屬性 (attribute) 只會在也標記上 [Parameter] 屬性 (attribute) 的屬性 (property) 上有效。 在設計階段以及在建置應用程式時,會強制執行 EditorRequiredAttribute。 屬性 (attribute) 不會在執行階段強制執行,而且也不會保證非 null 參數值。

[Parameter]
[EditorRequired]
public string? Title { get; set; }

也支援單行屬性 (attribute) 清單:

[Parameter, EditorRequired]
public string? Title { get; set; }

請勿在元件參數屬性上使用 required 修飾詞init 存取子。 元件通常會使用反映來具現化和指派參數值,這會略過 initrequired 旨在提供的保證。 請改用 [EditorRequired] 屬性以指定必要的元件參數。

請勿在元件參數屬性上使用 init 存取子,因為會使用反映將元件參數值設定為 ParameterView.SetParameterProperties,這會略過僅限 init 的 setter 限制。 使用 [EditorRequired] 屬性以指定必要的元件參數。

請勿在元件參數屬性上使用 init 存取子,因為會使用反映將元件參數值設定為 ParameterView.SetParameterProperties,這會略過僅限 init 的 setter 限制。

Tuples (API 文件) 支援用於元件參數和 RenderFragment 類型。 下列元件參數範例會在 Tuple 中傳遞三個值:

RenderTupleChild.razor

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor

@page "/render-tuple-parent"

<PageTitle>Render Tuple Parent</PageTitle>

<h1>Render Tuple Parent Example</h1>

<RenderTupleChild Data="data" />

@code {
    private (int, string, bool) data = new(999, "I aim to misbehave.", true);
}

支援具名 Tuple,如下列範例所示:

NamedTupleChild.razor

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

NamedTuples.razor

@page "/named-tuples"

<PageTitle>Named Tuples</PageTitle>

<h1>Named Tuples Example</h1>

<NamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

引用 ©2005 Universal Pictures: Serenity (Nathan Fillion)

Tuples (API 文件) 支援用於元件參數和 RenderFragment 類型。 下列元件參數範例會在 Tuple 中傳遞三個值:

RenderTupleChild.razor

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor

@page "/render-tuple-parent"

<h1>Render Tuple Parent</h1>

<RenderTupleChild Data="data" />

@code {
    private (int, string, bool) data = new(999, "I aim to misbehave.", true);
}

支援具名 Tuple,如下列範例所示:

RenderNamedTupleChild.razor

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

RenderNamedTupleParent.razor

@page "/render-named-tuple-parent"

<h1>Render Named Tuple Parent</h1>

<RenderNamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

引用 ©2005 Universal Pictures: Serenity (Nathan Fillion)

路由參數

元件可以在 @page 指示詞的路由範本中指定路由參數。 Blazor 路由器會使用路由參數來填入對應的元件參數。

RouteParameter1.razor

@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}

如需詳細資訊,請參閱 ASP.NET Core Blazor 路由和導覽路由參數一節。 此外也支援選擇性路由參數,同一節有其相關說明。 如需可跨多個資料夾界限來擷取路徑的 catch-all 路由參數 ({*pageRoute}) 的相關資訊,請參閱 ASP.NET Core Blazor 路由和導覽Catch-all 路由參數一節。

如需詳細資訊,請參閱 ASP.NET Core Blazor 路由和導覽路由參數一節。 不支援選擇性路由參數,因此需要兩個 @page 指示詞 (如需詳細資訊,請參閱路由參數一節)。 如需可跨多個資料夾界限來擷取路徑的 catch-all 路由參數 ({*pageRoute}) 的相關資訊,請參閱 ASP.NET Core Blazor 路由和導覽Catch-all 路由參數一節。

警告

透過預設啟用的壓縮,避免建立安全 (經過驗證/授權) 的互動式伺服器端元件來呈現來自不受信任來源的資料。 不受信任的來源包括路由參數、查詢字串、來自 JS 互通性的資料,以及第三方使用者可以控制的任何其他資料來源 (資料庫、外部服務)。 如需詳細資訊,請參閱 ASP.NET Core BlazorSignalR 指引ASP.NET Core Blazor 互動式伺服器端轉譯的威脅防護指引

子內容轉譯片段

元件可以設定另一個元件的內容。 指派方元件會提供子元件的開頭和結束標籤之間的內容。

在下列範例中,RenderFragmentChild 元件具有 ChildContent 元件參數,此參數代表要轉譯為 RenderFragment 的 UI 區段。 ChildContent 在元件 Razor 標記中的位置,正是內容在最終 HTML 輸出中的轉譯位置。

RenderFragmentChild.razor

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

重要

依照慣例,接收 RenderFragment 內容的屬性必須命名為 ChildContent

事件回呼不支援用於 RenderFragment

下列元件會藉由將內容放在子元件的開頭和結束標籤內,來提供用於轉譯 RenderFragmentChild 的內容。

RenderFragments.razor

@page "/render-fragments"

<PageTitle>Render Fragments</PageTitle>

<h1>Render Fragments Example</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

由於 Blazor 轉譯子內容的方式,如果 RenderFragmentChild 元件的內容中使用遞增迴圈變數,則在 for 迴圈內轉譯元件需要用到本機索引變數。 下列範例可以新增至上述父元件:

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

或者,使用 foreach 迴圈並搭配 Enumerable.Range,而不是使用 for 迴圈。 下列範例可以新增至上述父元件:

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

轉譯片段可用來在整個 Blazor 應用程式中轉譯子內容,下列文章和文章內的章節會有範例進行相關說明:

注意

Blazor 架構的內建 Razor 元件會使用相同的 ChildContent 元件參數慣例來設定其內容。 若要查看設定子內容的元件,請在 API 文件中搜尋元件參數屬性名稱 ChildContent (使用搜尋字詞「ChildContent」來篩選 API)。

轉譯可重複使用轉譯邏輯的片段

您可以透過單純重複使用轉譯邏輯的方式來分解子元件。 在任何元件的 @code 區塊中,定義 RenderFragment,並視需要從任何位置轉譯片段任意次數:

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo =  @<p>Welcome to your new app!</p>;
}

如需詳細資訊,請參閱重複使用轉譯邏輯

擷取元件的參考

元件參考可讓您參考用於發出命令的元件執行個體。 若要擷取元件參考:

  • @ref 屬性新增至子元件。
  • 定義與子元件相同類型的欄位。

元件轉譯時,欄位會填入元件執行個體。 接著,您便可以叫用執行個體上的 .NET 方法。

請考慮下列會在呼叫其 ChildMethod 時記錄訊息的 ReferenceChild 元件。

ReferenceChild.razor

@inject ILogger<ReferenceChild> Logger

@if (value > 0)
{
    <p>
        <code>value</code>: @value
    </p>
}

@code {
    private int value;

    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);

        this.value = value;
        StateHasChanged();
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

只有在元件已轉譯且其輸出包含 ReferenceChild 的元素之後,才會填入元件參考。 在元件轉譯之前,不會有對象可以參考。 請勿嘗試直接呼叫事件處理常式的參考元件方法 (例如 @onclick="childComponent!.ChildMethod(5)"),因為在指派點擊事件時可能尚未指派參考變數。

若要在元件完成轉譯之後操作元件參考,請使用 OnAfterRenderOnAfterRenderAsync 方法

下列範例使用上述 ReferenceChild 元件。

ReferenceParent.razor

@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}

雖然擷取元件參考會使用類似於擷取元素參考的語法,但擷取元件參考不是 JavaScript 的 Interop 功能。 元件參考不會傳遞至 JavaScript 程式碼。 元件參考只會用於 .NET 程式碼。

重要

請勿使用元件參考來變動子元件的狀態。 請改用一般宣告式元件參數,將資料傳遞至子元件。 使用元件參數會導致子元件在正確時間自動重新轉譯。 如需詳細資訊,請參閱元件參數一節和 ASP.NET Core Blazor 資料繫結一文。

套用屬性

您可以使用 @attribute 指示詞將屬性套用至元件。 下列範例會將 [Authorize] 屬性套用至元件的類別:

@page "/"
@attribute [Authorize]

條件式 HTML 元素屬性

HTML 元素屬性 (attribute) 屬性 (property) 會根據 .NET 值有條件地進行設定。 如果值為 falsenull,則不會設定屬性 (property)。 如果值為 true,則會設定屬性 (property)。

在下列範例中,IsCompleted 會判斷是否已設定 <input> 元素的 checked 屬性 (property)。

ConditionalAttribute.razor

@page "/conditional-attribute"

<PageTitle>Conditional Attribute</PageTitle>

<h1>Conditional Attribute Example</h1>

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

如需詳細資訊,請參閱 ASP.NET Core 的 Razor 語法參考

警告

aria-pressed 之類的某些 HTML 屬性 (attribute) 在 .NET 類型為 bool 時無法正常運作。 在這些情況下,請使用 string 類型,而非使用 bool

原始 HTML

字串一般會使用 DOM 文字節點來進行轉譯,這表示其可能包含的任何標記都會遭到忽略並視為常值文字。 若要轉譯原始 HTML,請將 HTML 內容包裝到 MarkupString 值內。 此值會剖析為 HTML 或 SVG,並插入到 DOM。

警告

針對從任何不受信任的來源所建構的原始 HTML 進行轉譯會有安全性風險,請一律避免此行為。

下列範例示範如何使用 MarkupString 類型,將靜態 HTML 內容的區塊新增至元件的轉譯輸出。

MarkupStrings.razor

@page "/markup-strings"

<PageTitle>Markup Strings</PageTitle>

<h1>Markup Strings Example</h1>

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor 範本

您可以使用 Razor 範本語法來定義 UI 程式碼片段,從而定義轉譯片段。 Razor 範本會使用下列格式:

@<{HTML tag}>...</{HTML tag}>

下列範例說明如何直接在元件中指定 RenderFragmentRenderFragment<TValue> 的值並轉譯範本。 轉譯片段也可以作為引數傳遞至樣板化元件

RazorTemplate.razor

@page "/razor-template"

<PageTitle>Razor Template</PageTitle>

<h1>Razor Template Example</h1>

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

上述程式碼的轉譯輸出:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

靜態資產

Blazor 會遵循靜態資產的 ASP.NET Core 應用程式慣例。 靜態資產位於專案的 web root (wwwroot) 資料夾內,或是 wwwroot 資料夾底下的資料夾內。

請使用基底相對路徑 (/) 來參考靜態資產的 Web 根目錄。 在下列範例中,logo.png 實際位於 {PROJECT ROOT}/wwwroot/images 資料夾中。 {PROJECT ROOT} 是應用程式的專案根目錄。

<img alt="Company logo" src="/images/logo.png" />

元件支援波狀符號正斜線標記法 (~/)。

如需如何設定應用程式基底路徑的相關資訊,請參閱裝載及部署 ASP.NET Core Blazor

元件不支援標籤協助程式

元件不支援Tag Helpers。 若要在 Blazor 中提供類似標籤協助程式的功能,請建立功能與標籤協助程式相同的元件,並改用該元件。

可縮放向量圖形 (SVG) 影像

由於 Blazor 會轉譯 HTML,因此包括可縮放向量圖形 (SVG) 影像 (.svg) 在內的瀏覽器支援影像可透過 <img> 標籤而獲得支援:

<img alt="Example image" src="image.svg" />

同樣地,樣式表檔案的 CSS 規則 (.css) 也支援 SVG 影像:

.element-class {
    background-image: url("image.svg");
}

Blazor 會支援 <foreignObject> 元素,以便顯示 SVG 內的任意 HTML。 標記可以代表任意 HTML、RenderFragment 或 Razor 元件。

下列範例會示範:

  • 顯示 string (@message)。
  • <input> 元素和 value 欄位的雙向繫結。
  • Robot 元件。
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" 
        fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject width="200" height="200">
        <label>
            Two-way binding:
            <input @bind="value" @bind:event="oninput" />
        </label>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject>
        <Robot />
    </foreignObject>
</svg>

@code {
    private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
        "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    private string? value;
}

空白字元轉譯行為

除非 @preservewhitespace 指示詞與 true 值搭配使用,否則如果有下列情況,系統預設會移除額外的空白字元:

  • 在元素內前置或後置。
  • RenderFragment/RenderFragment<TValue> 參數內前置或後置 (例如,子內容傳遞至另一個元件)。
  • 其在 C# 程式碼區塊之前或之後,例如 @if@foreach

使用 CSS 規則時,移除空白字元可能會影響經過轉譯的輸出,例如 white-space: pre。 若要停用此效能最佳化功能並保留空白字元,請採取下列其中一個動作:

  • 在 Razor 檔案 (.razor) 頂端新增 @preservewhitespace true 指示詞,以將喜好設定套用至特定元件。
  • _Imports.razor 檔案內新增 @preservewhitespace true 指示詞,以將喜好設定套用至子目錄或整個專案。

在大部分情況下,您不需要採取任何動作,因為應用程式通常會繼續正常運作 (但速度會更快)。 如果移除空白字元會導致特定元件發生轉譯問題,請在該元件中使用 @preservewhitespace true 來停用此最佳化。

空白字元會保留在元件的來源標記中。 只有空白字元的文字會在瀏覽器的 DOM 中轉譯,即使沒有視覺效果也是如此。

請考慮下列元件標記:

<ul>
    @foreach (var item in Items)
    {
        <li>
            @item.Text
        </li>
    }
</ul>

上述範例會轉譯下列不必要的空白字元:

  • 位於 @foreach 程式碼區塊外部。
  • <li> 元素周圍。
  • @item.Text 輸出周圍。

100 個項目的清單會產生超過 400 個空白字元區域。 再多的空白字元也不會在視覺上影響轉譯的輸出。

在轉譯元件的靜態 HTML 時,不會保留標籤內的空白字元。 例如,在元件 Razor 檔案中檢視下列 <img> 標籤的轉譯輸出 (.razor):

<img     alt="Example image"   src="img.png"     />

不會從上述標記保留空白字元:

<img alt="Example image" src="img.png" />

根元件

根 Razor 元件 (根元件) 是在應用程式所建立的任何元件階層中第一個載入的元件。

在從 Blazor Web 應用程式專案範本建立的應用程式中,App 元件 (App.razor) 會針對伺服器端 Program 檔案中呼叫 MapRazorComponents<TRootComponent> 所宣告的類型參數,指定為預設根元件。 下列範例示範使用 App 元件做為根元件,這是從 Blazor 專案範本所建立應用程式的預設值:

app.MapRazorComponents<App>();

注意

不支援讓根元件成為互動式,例如 App 元件。

在從 Blazor Server 專案範本建立的應用程式中,App 元件 (App.razor) 會使用元件標籤協助程式,在 Pages/_Host.cshtml 中指定為預設根元件:

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

在從 Blazor WebAssembly 專案範本建立的應用程式中,App 元件 (App.razor) 會指定為 Program 檔案中的預設根元件:

builder.RootComponents.Add<App>("#app");

在上述程式碼中,CSS 選取器 (#app) 表示已使用 appid 針對 wwwroot/index.html 中的 <div> 指定 App 元件:

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

MVC 和 Razor Pages 應用程式也可以使用元件標籤協助程式來註冊靜態轉譯的 Blazor WebAssembly 根元件:

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

靜態轉譯的元件只能新增至應用程式。 之後無法移除或更新這些元件。

如需詳細資訊,請參閱以下資源: