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

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

元件是使用 C# 和 HTML 標籤的組合,在副檔名為的元件檔案 .razor Razor 實作。

Razor 語法

元件使用 Razor 語法 。 元件、 指示詞和指示 屬性 廣泛使用兩 Razor 個功能。 這些是前面加上 @ 標記的 Razor 保留關鍵字:

  • 指示詞 :變更剖析元件標記或函式的方式。 例如, @page 指示詞會指定具有路由範本的可路由元件,而且可以透過使用者在瀏覽器中的特定 URL 要求直接連線。
  • 指示詞屬性 :變更元件專案剖析或函式的方式。 例如,元素的 <input> 指示詞屬性會將 @bind 資料系結至元素的值。
  • 本文和檔集的其他文章 Blazor 會進一步說明元件中使用的指示詞和指示詞屬性。 如需語法的一 Razor 般資訊,請參閱 Razor ASP.NET Core的語法參考

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

  • ProductDetail.razor 有效。
  • productDetail.razor 無效。
  • 檔中使用的 Blazor 常見 Blazor 命名慣例包括:

  • 元件檔案路徑會使用 Pascal 案例†並出現在顯示元件程式碼範例之前。 路徑表示典型的資料夾位置。 例如, Pages/ProductDetail.razor 表示 ProductDetail 元件具有 的 ProductDetail.razor 檔案名,且位於 Pages 應用程式的 資料夾中。
  • 可路由傳送元件的元件檔案路徑會與其 URL 相符,並顯示元件路由範本中單字之間的空格。 例如, ProductDetail 在相對 URL /product-detail 的瀏覽器中要求具有路由範本 ( /product-detail @page "/product-detail" ) 的元件。
  • †Pascal 大小寫 (大寫) 是不含空格和標點符號的命名慣例,且每個字的第一個字母大寫,包括第一個字。

    藉由提供路由範本給應用程式中每個可存取的元件以及 指示 @page 詞,即可達成中的 Blazor 路由。 Razor編譯具有 @page 指示詞的檔案時,產生的類別會指定 RouteAttribute 路由範本。 在執行時間,路由器會搜尋具有 的 RouteAttribute 元件類別,並轉譯任何元件具有符合所要求 URL 的路由範本。

    下列 HelloWorld 元件使用 的 /hello-world 路由範本。 元件的轉譯網頁會到達相對 URL /hello-world 。 使用預設通訊協定、主機和埠在本機執行 Blazor 應用程式時,會在 HelloWorld 瀏覽器中 https://localhost:5001/hello-world 要求元件。 產生網頁的元件通常位於 Pages 資料夾中,但您可以使用任何資料夾來保存元件,包括巢狀資料夾內。

    Pages/HelloWorld.razor :

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

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

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

    元件的 UI 是使用 Razor 語法 來定義,其中包含 Razor 標記、C# 和 HTML。 編譯應用程式時,HTML 標籤和 C# 轉譯邏輯會轉換成元件類別。 產生的類別名稱符合檔案名。

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

  • 屬性和欄位初始化運算式。
  • 父元件和路由參數所傳遞引數的參數值。
  • 使用者事件處理、生命週期事件和自訂群組件邏輯的方法。
  • 元件成員用於使用以符號開頭的 @ C# 運算式來轉譯邏輯。 例如,C# 欄位會以功能變數名稱前置 @ 詞呈現。 下列 Markup 元件會評估並轉譯:

  • headingFontStyle 表示標題專案的 CSS 屬性值 font-style
  • headingText 為標題專案的內容。
  • Pages/Markup.razor :

    @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# 類別 ,而且可以在專案內的任何位置放置。 產生網頁的元件通常位於 Pages 資料夾中。 非頁面元件通常會放在 Shared 資料夾或新增至專案的自訂資料夾。

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

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

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

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

    請考慮下列 Heading 元件,可供其他元件用來顯示標題。

    Shared/Heading.razor :

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

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

    Pages/HeadingExample.razor :

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

    如果元件包含一個 HTML 元素,其大寫第一個字母不符合相同命名空間內的元件名稱,就會發出警告,指出元素有非預期的名稱。 @using 新增元件的命名空間指示詞可讓元件使用,以解析警告。 如需詳細資訊,請參閱 命名空間一 節。

    Heading 本節中顯示的元件範例沒有 @page 指示詞,因此 Heading 元件無法透過瀏覽器中的直接要求直接存取使用者。 不過,任何具有 指示詞的元件都可以在另一個 @page 元件中巢狀。 Heading 如果直接存取元件的方式是在其檔案頂端 Razor 包含 @page "/heading" ,則會針對 和 /heading-example /heading 瀏覽器要求轉譯元件。

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

  • 元件的 Counter 命名空間為 BlazorSample.Pages
  • 元件的完整型別名稱為 BlazorSample.Pages.Counter
  • 對於保存元件的自訂資料夾,請將 指示詞新增 @using 至父元件或應用程式檔案 _Imports.razor 。 下列範例會讓資料夾中的 Components 元件可供使用:

    @using BlazorSample.Components
    

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

    元件也可以使用其完整名稱來參考,而不需要 @using 指示詞。 下列範例會直接參考 ProductDetail 應用程式資料夾中的元件 Components

    <BlazorSample.Components.ProductDetail />
    

    所撰寫 Razor 元件的命名空間是以下列優先順序 (為基礎) :

  • @namespace例如, @namespace BlazorSample.CustomNamespace 檔案標記中的 Razor 指示詞 () 。
  • 例如,專案檔中的專案 RootNamespace () <RootNamespace>BlazorSample</RootNamespace>
  • 專案名稱,取自專案檔的檔案名 () .csproj ,以及專案根目錄到元件的路徑。 例如,架構會 {PROJECT ROOT}/Pages/Index.razor 使用 () BlazorSample.csproj 的專案命名空間 BlazorSample 解析為元件的命名空間 BlazorSample.PagesIndex{PROJECT ROOT} 是專案根路徑。 元件遵循 C# 名稱系結規則。 Index在此範例中的元件中,範圍中的元件都是所有元件:
    • 在相同的資料夾中, Pages
    • 專案根目錄中未明確指定不同命名空間的元件。
    • 不支援下列各項:

    • 資格 global::
    • 使用別名語句匯入 using 元件。 例如,不支援 @using Foo = Bar
    • 部分限定名稱。 例如,您無法新增 @using BlazorSample 至元件,然後使用 參考 NavMenu 應用程式 Shared 資料夾中的元件 (Shared/NavMenu.razor) <Shared.NavMenu></Shared.NavMenu>
    • 部分類別支援

      元件會產生為 C# 部分類別 ,並使用下列其中一種方法來撰寫:

    • 單一檔案包含一或多個 @code 區塊、HTML 標籤和 Razor 標記中定義的 C# 程式碼。 Blazor 專案範本會使用此單一檔案方法來定義其元件。
    • HTML 和 Razor 標記會放在 Razor 檔案 (.razor) 中。 C# 程式碼會放在定義為部分類別的程式碼後置檔案中, (.cs) 。
    • 定義元件特定樣式的元件樣式表單是個別的檔案 (.css) 。 BlazorCSS 隔離稍後會在ASP.NET CORE Blazor CSS 隔離中說明。

      下列範例顯示預設 Counter 元件,其中包含 @code 從 Blazor 專案範本產生之應用程式中的區塊。 標記和 C# 程式碼位於相同的檔案中。 這是元件撰寫中最常見的方法。

      Pages/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++;

      下列 Counter 元件會使用具有部分類別的程式碼後置檔案,從 C# 程式碼分割 HTML 和 Razor 標記:

      Pages/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>

      Pages/CounterPartialClass.razor.cs:

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

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

      元件所使用的一般命名空間:

      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;
      

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

      using BlazorSample;
      using BlazorSample.Shared;
      

      指示 @inherits 詞是用來指定元件的基類。 下列範例示範元件如何繼承基類,以提供元件的屬性和方法。 基 BlazorRocksBase 類衍生自 ComponentBase

      Pages/BlazorRocks.razor:

      @page "/blazor-rocks" @inherits BlazorRocksBase <h1>@BlazorRocksText</h1>

      BlazorRocksBase.cs:

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

      元件參數會將資料傳遞至元件,並使用具有 屬性之元件類別[Parameter]上的公用C# 屬性來定義。 在下列範例中,內建的參考類型 () System.String 和使用者定義的參考類型 (PanelBody) 會傳遞為元件參數。

      PanelBody.cs:

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

      Shared/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 @code { [Parameter] public string Title { get; set; } = "Set By Child"; [Parameter] public PanelBody Body { get; set; } = new() Text = "Set by child.", Style = "normal"

      支援提供元件參數的初始值,但不會建立在第一次轉譯元件之後寫入其自有參數的元件。 如需詳細資訊,請參閱本文的 覆寫參數 一節。

      元件的 TitleBody 元件參數 ParameterChild 是由 HTML 標籤中的引數所設定,可轉譯元件的實例。 下列 ParameterParent 元件會轉譯兩 ParameterChild 個元件:

    • 第一個 ParameterChild 元件會轉譯,而不提供參數引數。
    • 第二 ParameterChild 個元件會接收 和 BodyParameterParent 元件的值 Title ,其會使用明確的 C# 運算式來設定 屬性的值 PanelBody
    • Pages/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 元件的預設值。

      為了清楚起見,轉譯的 CSS 樣式類別不會顯示在下列轉譯的 HTML 標籤中。

      <h1>Child component (without attribute values)</h1>
          <div>Set By Child</div>
          <div>Set by child.</div>
      <h1>Child component (with attribute values)</h1>
          <div>Set by Parent</div>
          <div>Set by parent.</div>
      

      使用Razor 保留 @ 符號,將方法的 C# 欄位、屬性或結果指派給元件參數做為 HTML 屬性值。 下列 ParameterParent2 元件會顯示上述 ParameterChild 元件的四個實例,並將其參數值設定 Title 為:

    • 欄位的值 title
    • C# 方法的結果 GetTitle
    • 目前格式為 long 格式 ToLongDateString 的本機日期,其使用 隱含 C# 運算式
    • 物件的 panelDataTitle 屬性。
    • 字串 @ 參數需要前置詞。 否則,架構會假設已設定字串常值。

      在字串參數之外,我們建議將前置詞用於 @ 非精簡字元,即使它們並非嚴格必要也一樣。

      我們 @ 不建議使用常值前置詞 (例如布林值) 、關鍵字 (例如、 this) 或 null ,但您可以選擇使用它們。 例如, IsFixed="@true" 並不常見,但受到支援。

      在大部分情況下,每個 HTML5 規格的參數屬性值引號都是選擇性的。 例如, Value=this 支援 ,而不是 Value="this" 。 不過,我們建議使用引號,因為更容易記住並廣泛採用 Web 技術。

      在整個檔中,程式碼範例:

    • 一律使用引號。 範例: Value="this".
    • 非音元一律使用 @ 前置詞,即使它是選擇性的。 範例: Title="@title" ,其中 title 是字串類型的變數。 Count="@ct",其中 ct 是數位型別變數。
    • 運算式外部的 Razor 常值一律會避免 @ 。 範例: IsFixed="true".
    • Pages/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";

      將 C# 成員指派給元件參數時,請在成員前面加上 @ 符號,且永遠不會在參數的 HTML 屬性前面加上前置詞。

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

      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 」 與物件的屬性值串連。 雖然頁面 () .cshtml 支援 Razor 此語法,但對元件中子參數的 Title 指派無效。 不支援下列 Razor 語法:

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

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

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

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

      Pages/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";

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

      支援提供元件參數的初始值,但不會建立在第一次轉譯元件之後寫入其自有參數的元件。 如需詳細資訊,請參閱本文的 覆寫參數 一節。

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

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

      請勿將自訂邏輯放在 或 set 存取子中 get ,因為元件參數只是用來作為父元件的通道,以便將資訊流向子元件。 set如果子元件屬性的存取子包含導致重新呈現父元件的邏輯,則無限轉譯迴圈會產生。

      若要轉換收到的參數值:

    • 將參數屬性保留為自動屬性,以代表提供的原始資料。
    • 建立不同的屬性或方法,以根據參數屬性提供轉換的資料。
    • 覆寫 OnParametersSetAsync 以在每次收到新資料時轉換接收的參數。

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

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

      初始指派 DateTime.Now 之後, 請勿 在開發人員程式碼中將值指派給 StartData 。 如需詳細資訊,請參閱本文的 覆寫參數 一節。

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

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

      也支援單行屬性清單:

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

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

      Shared/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">
                  <li>Integer: @Data?.Item1</li>
                  <li>String: @Data?.Item2</li>
                  <li>Boolean: @Data?.Item3</li>
      @code {
          [Parameter]
          public Tuple<int, string, bool>? Data { get; set; }
      

      Pages/RenderTupleParent.razor:

      @page "/render-tuple-parent"
      <h1>Render <code>Tuple</code> Parent</h1>
      <RenderTupleChild Data="@data" />
      @code {
          private Tuple<int, string, bool> data = new(999, "I aim to misbehave.", true);
      

      元件中 Razor 僅支援 C# 7.0 或更新版本的未命名 Tuple。 元件中的 Razor具名 Tuple支援計畫在未來 ASP.NET Core版本。 如需詳細資訊,請參閱Blazor 名為 Tuples (dotnet/aspnetcore #28982) 的 Transpiler 問題

      引述 ©2005 通用圖片Serenity (Nathan Fillion)

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

      支援選擇性路由參數。 在下列範例中 text ,選擇性參數會將路由區段的值指派給元件的 Text 屬性。 如果區段不存在,生命週期方法中的 OnInitializedText 會設定為 「 fantastic 」。

      Pages/RouteParameter.razor:

      @page "/route-parameter/{text?}" <h1>Blazor is @Text!</h1> @code { [Parameter] public string? Text { get; set; } protected override void OnInitialized() Text = Text ?? "fantastic";

      如需擷取所有路由參數 ({*pageRoute}) 的相關資訊,可擷取跨多個資料夾界限的路徑,請參閱ASP.NET Core Blazor 路由和導覽

      子內容轉譯片段

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

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

      Shared/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> @code { [Parameter] public RenderFragment? ChildContent { get; set; }

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

      不支援 RenderFragment事件回呼

      下列 RenderFragmentParent 元件提供轉譯的內容,方法是將內容 RenderFragmentChild 放在子元件的開頭和結束記號內。

      Pages/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 轉譯元件需要局部索引變數。 下列範例可以新增至上述 RenderFragmentParent 元件:

      <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 迴圈。 下列範例可以新增至上述 RenderFragmentParent 元件:

      <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 佈局
    • 跨元件階層傳遞資料
    • 樣板化元件
    • 全域例外狀況處理
    • Blazor 架構的 內 Razor 建元件 使用相同的 ChildContent 元件參數慣例來設定其內容。 您可以在 API 檔中搜尋元件參數屬性名稱 ChildContent 來查看設定子內容的元件 , (篩選 API 與搜尋字詞 「ChildContent」)

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

      您可以將子元件完全分解為重複使用轉譯邏輯的方式。 在任何元件的 區塊中 @code ,定義 RenderFragment ,並視需要從任何位置轉譯片段:

      <h1>Hello, world!</h1>
      @RenderWelcomeInfo
      <p>Render the welcome info a second time:</p>
      @RenderWelcomeInfo
      @code {
          private RenderFragment RenderWelcomeInfo = __builder =>
              <p>Welcome to your new app!</p>
      

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

      覆寫的參數

      架構 Blazor 通常會強制使用安全的父子參數指派:

    • 參數不會意外覆寫。
    • 副作用會最小化。 例如,會避免其他轉譯,因為它們可能會建立無限轉譯迴圈。
    • 當父元件重新呈現時,子元件會收到可能會覆寫現有值的新參數值。 使用一或多個資料繫結參數開發元件時,通常會意外覆寫子元件中的參數值,而開發人員會直接寫入子系中的參數:

    • 子元件會以父元件中的一或多個參數值轉譯。
    • 子系會直接寫入參數的值。
    • 父元件會重新呈現並覆寫子參數的值。
    • 覆寫參數值的可能性也會延伸到子元件的屬性 set 存取子。

      我們的一般指引不是在第一次轉譯元件之後,直接寫入自己的參數的元件。

      請考慮下列 Expander 元件:

    • 呈現子內容。
    • 使用元件參數 () Expanded 顯示子內容的切換。
    • 在下列 Expander 元件示範覆寫的參數之後,會顯示已修改 Expander 的元件,以示範此案例的正確方法。 下列範例可以放在本機範例應用程式中,以體驗所述的行為。

      Shared/Expander.razor:

      <div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem"> <div class="card-body"> <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2> @if (Expanded) <p class="card-text">@ChildContent</p> @code { [Parameter] public bool Expanded { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } private void Toggle() Expanded = !Expanded;

      元件 Expander 會新增至下列 ExpanderExample 可能呼叫 StateHasChanged 的父元件:

    • 在開發人員程式碼中呼叫 StateHasChanged 會通知元件其狀態已變更,而且通常會觸發元件重新呈現以更新 UI。 StateHasChanged稍後會在ASP.NET Core Razor 元件生命週期ASP.NET Core Razor 元件轉譯中詳細說明。
    • 按鈕的 @onclick 指示詞屬性會將事件處理常式附加至按鈕的事件 onclick 。 事件處理會在稍後 ASP.NET Core事件處理中 Blazor更詳細地討論。
    • Pages/ExpanderExample.razor:

      @page "/expander-example" <Expander Expanded="true"> Expander 1 content </Expander> <Expander Expanded="true" /> <button @onclick="StateHasChanged"> Call StateHasChanged </button>

      一開始, Expander 元件會在切換其 Expanded 屬性時獨立運作。 子元件會如預期般維護其狀態。

      如果在 StateHasChanged 父元件中呼叫 ,則如果架構的參數可能已變更,架構 Blazor 會重新呈現子元件:

    • 針對明確檢查的參數類型 Blazor 群組,如果子元件偵測到任何參數已變更, Blazor 請重新呈現子元件。
    • 針對未核取的參數類型,不論參數是否已變更, Blazor 都會重新呈現子元件。 子內容屬於這個參數類型類別,因為子內容的類型為 RenderFragment ,這是參考其他可變動物件的委派。
    • ExpanderExample針對元件:

    • 第一 Expander 個元件會在可能 RenderFragment 變動的 中設定子內容,因此在父元件中呼叫 StateHasChanged 會自動重新呈現元件,並可能覆寫 Expanded 的值至 其初始化值 true
    • 第二個 Expander 元件不會設定子內容。 因此,可能可變 RenderFragment 不存在。 父元件中的 呼叫 StateHasChanged 不會自動重新呈現子元件,因此不會覆寫元件 Expanded 的值。
    • 若要在上述案例中維護狀態,請使用元件中的 Expander私用欄位來維護其切換狀態。

      下列修訂的 Expander 元件:

    • Expanded接受父代的元件參數值。
    • 將元件參數值指派給事件中的OnInitialized私用欄位 (expanded) 。
    • 使用私用欄位來維護其內部切換狀態,示範如何避免直接寫入參數。
    • 本節中的建議延伸至元件參數 set 存取子中的類似邏輯,這可能會導致類似的非預期副作用。

      Shared/Expander.razor:

      <div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem"> <div class="card-body"> <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2> @if (expanded) <p class="card-text">@ChildContent</p> @code { private bool expanded; [Parameter] public bool Expanded { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } protected override void OnInitialized() expanded = Expanded; private void Toggle() expanded = !expanded;

      如需雙向父子系結範例,請參閱ASP.NET Core資料系結 Blazor。 如需詳細資訊,請參閱Blazor dotnet/aspnetcore #24599) (雙向系結錯誤

      如需變更偵測的詳細資訊,請參閱 BlazorASP.NET Core Razor 元件轉譯

      屬性展開和任意參數

      除了元件的宣告參數之外,元件還可以擷取和轉譯其他屬性。 您可以在字典中擷取其他屬性,然後在使用 @attributesRazor 指示詞屬性轉譯元件時,在元素上展開。 此案例適用于定義產生支援各種自訂之標記專案的元件。 例如,為支援許多參數的 個別 <input> 定義屬性可能很麻煩。

      在下列 Splat 元件中:

    • 第一個 <input> 元素 (id="useIndividualParams") 使用個別元件參數。
    • 第二 <input> 個元素 () id="useAttributesDict" 使用屬性 splatting。
    • Pages/Splat.razor:

      @page "/splat" <input id="useIndividualParams" maxlength="@maxlength" placeholder="@placeholder" required="@required" size="@size" /> <input id="useAttributesDict" @attributes="InputAttributes" /> @code { private string maxlength = "10"; private string placeholder = "Input placeholder text"; private string required = "required"; private string size = "50"; private Dictionary<string, object> InputAttributes { get; set; } = new() { "maxlength", "10" }, { "placeholder", "Input placeholder text" }, { "required", "required" }, { "size", "50" }

      網頁中的轉譯 <input> 元素完全相同:

      <input id="useIndividualParams"
             maxlength="10"
             placeholder="Input placeholder text"
             required="required"
             size="50">
      <input id="useAttributesDict"
             maxlength="10"
             placeholder="Input placeholder text"
             required="required"
             size="50">
      

      若要接受任意屬性,請定義 元件參數 ,並將 CaptureUnmatchedValues 屬性設定為 true

      @code {
          [Parameter(CaptureUnmatchedValues = true)]
          public Dictionary<string, object>? InputAttributes { get; set; }
      

      上的 CaptureUnmatchedValues[Parameter] 屬性可讓 參數符合不符合任何其他參數的所有屬性。 元件只能使用 CaptureUnmatchedValues 定義單一參數。 與 搭配 CaptureUnmatchedValues 使用的屬性類型必須具有字串索引鍵的可指派。 Dictionary<string, object> IEnumerable<KeyValuePair<string, object>>在此案例中使用 或 IReadOnlyDictionary<string, object> 也是選項。

      相對於專案屬性位置的位置 @attributes 而言,的位置很重要。 在 元素上進行擷取時 @attributes ,屬性會從右至左處理 (最後一個) 。 請考慮使用子元件的父元件範例如下:

      Shared/AttributeOrderChild1.razor:

      <div @attributes="AdditionalAttributes" extra="5" /> @code { [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object>? AdditionalAttributes { get; set; }

      Pages/AttributeOrderParent1.razor:

      @page "/attribute-order-parent-1" <AttributeOrderChild1 extra="10" />

      extra元件的 AttributeOrderChild1 屬性設定為 的 @attributes 右邊。 當傳遞其他屬性時,元件的 AttributeOrderParent1<div> 譯會包含 extra="5" ,因為屬性會由右至左處理, (最後一個) :

      <div extra="5" />
      

      在下列範例中,和 @attributes 的順序 extra 會在子元件的 <div> 中反轉:

      Shared/AttributeOrderChild2.razor:

      <div extra="5" @attributes="AdditionalAttributes" /> @code { [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object>? AdditionalAttributes { get; set; }

      Pages/AttributeOrderParent2.razor:

      @page "/attribute-order-parent-2" <AttributeOrderChild2 extra="10" />

      <div>在父元件的轉譯網頁中,透過其他屬性傳遞時,會 extra="10" 包含 :

      <div extra="10" />
      

      擷取元件的參考

      元件參考提供方法來參考發出命令的元件實例。 若要擷取元件參考:

    • @ref將屬性新增至子元件。
    • 定義與子元件相同類型的欄位。
    • 轉譯元件時,欄位會填入元件實例。 然後,您可以在 實例上叫用 .NET 方法。

      請考慮下列 ReferenceChild 元件,以在呼叫訊息 ChildMethod 時記錄訊息。

      Shared/ReferenceChild.razor:

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

      只有在轉譯元件之後,才會填入元件參考,且其輸出包含 ReferenceChild 的 元素。 在轉譯元件之前,不會參考任何專案。

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

      若要搭配事件處理常式使用參考變數,請使用 Lambda 運算式,或在OnAfterRenderAsync 方法中 OnAfterRender指派事件處理常式委派。 這可確保在指派事件處理常式之前指派參考變數。

      下列 Lambda 方法會使用上述 ReferenceChild 元件。

      Pages/ReferenceParent1.razor:

      @page "/reference-parent-1" <button @onclick="@(() => childComponent?.ChildMethod(5))"> Call <code>ReferenceChild.ChildMethod</code> with an argument of 5 </button> <ReferenceChild @ref="childComponent" /> @code { private ReferenceChild? childComponent;

      下列委派方法會使用上述 ReferenceChild 元件。

      Pages/ReferenceParent2.razor:

      @page "/reference-parent-2" <button @onclick="@(() => callChildMethod?.Invoke())"> Call <code>ReferenceChild.ChildMethod</code> with an argument of 5 </button> <ReferenceChild @ref="childComponent" /> @code { private ReferenceChild? childComponent; private Action? callChildMethod; protected override void OnAfterRender(bool firstRender) if (firstRender) callChildMethod = CallChildMethod; private void CallChildMethod() childComponent?.ChildMethod(5);

      雖然擷取元件參考使用類似的語法來 擷取專案參考,但擷取元件參考不是 JavaScript Interop 功能。 元件參考不會傳遞至 JavaScript 程式碼。 元件參考僅適用于 .NET 程式碼。

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

      同步處理內容

      Blazor 會使用同步處理內容 (SynchronizationContext) 來強制執行單一邏輯執行緒的執行。 元件的 生命週期方法和 引發 Blazor 的事件回呼會在同步處理內容上執行。

      Blazor Server的同步處理內容會嘗試模擬單一線程環境,使其與瀏覽器中的 WebAssembly 模型緊密相符,也就是單一執行緒。 在任何指定的時間點上,工作都是在一個執行緒上執行,這會產生單一邏輯執行緒的印象。 沒有任何兩個作業同時執行。

      避免執行緒封鎖呼叫

      一般而言,請勿在元件中呼叫下列方法。 下列方法會封鎖執行執行緒,進而封鎖應用程式繼續工作,直到基礎 Task 完成為止:

    • Result
    • WaitAny
    • WaitAll
    • Sleep
    • GetResult
    • Blazor 使用本節所述的執行緒封鎖方法的檔範例只會使用 方法進行示範,而不是建議的程式碼撰寫指引。 例如,一些元件程式碼示範會藉由呼叫 Thread.Sleep 來模擬長時間執行的進程。

      在外部叫用元件方法以更新狀態

      在事件中,元件必須根據外來事件進行更新,例如計時器或其他通知,請使用 InvokeAsync 方法,將程式碼執行分派至 Blazor 的同步處理內容。 例如,請考慮下列通知 通知程式服務 ,以通知任何接聽元件有關更新狀態。 Update您可以從應用程式中的任何位置呼叫 方法。

      TimerService.cs:

      public class TimerService : IDisposable private int elapsedCount; private readonly static TimeSpan heartbeatTickRate = TimeSpan.FromSeconds(5); private readonly ILogger<TimerService> logger; private readonly NotifierService notifier; private PeriodicTimer? timer; public TimerService(NotifierService notifier, ILogger<TimerService> logger) this.notifier = notifier; this.logger = logger; public async Task Start() if (timer is null) timer = new(heartbeatTickRate); logger.LogInformation("Started"); using (timer) while (await timer.WaitForNextTickAsync()) elapsedCount += 1; await notifier.Update("elapsedCount", elapsedCount); logger.LogInformation($"elapsedCount: {elapsedCount}"); public void Dispose() timer?.Dispose();

      NotifierService.cs:

      public class NotifierService public async Task Update(string key, int value) if (Notify != null) await Notify.Invoke(key, value); public event Func<string, int, Task>? Notify;

      註冊服務:

    • Blazor WebAssembly在應用程式中,在 中 Program.cs 將服務註冊為單一:

      builder.Services.AddSingleton<NotifierService>();
      builder.Services.AddSingleton<TimerService>();
      
    • Blazor Server在應用程式中,將服務註冊為 的範圍: Program.cs

      builder.Services.AddScoped<NotifierService>();
      builder.Services.AddScoped<TimerService>();
      

      NotifierService使用 來更新元件。

      Pages/ReceiveNotifications.razor:

      @page "/receive-notifications" @implements IDisposable @inject NotifierService Notifier @inject TimerService Timer <h1>Receive Notifications</h1> <h2>Timer Service</h2> <button @onclick="StartTimer">Start Timer</button> <h2>Notifications</h2> Status: @if (lastNotification.key is not null) <span>@lastNotification.key = @lastNotification.value</span> <span>Awaiting first notification</span> @code { private (string key, int value) lastNotification; protected override void OnInitialized() Notifier.Notify += OnNotify; public async Task OnNotify(string key, int value) await InvokeAsync(() => lastNotification = (key, value); StateHasChanged(); private async Task StartTimer() await Timer.Start(); public void Dispose() Notifier.Notify -= OnNotify;

      在上述範例中:

    • NotifierService在同步處理內容之外 Blazor 叫用元件的 OnNotify 方法。 InvokeAsync 用來切換至正確的內容,並將轉譯排入佇列。 如需詳細資訊,請參閱ASP.NET Core Razor 元件轉譯
    • 元件會實作 IDisposable 。 委派 OnNotify 在 方法中 Dispose 取消訂閱,在處置元件時由架構呼叫。 如需詳細資訊,請參閱ASP.NET Core Razor 元件生命週期
    • 用來 @key 控制專案和元件的保留

      轉譯專案或元件清單以及後續變更的專案或元件時,必須決定可以保留哪些先前的專案或元件, Blazor 以及模型物件應該如何對應至它們。 一般而言,此程式是自動的,而且可以忽略,但在某些情況下,您可能會想要控制進程。

      請考慮下列 DetailsPeople 元件:

    • 元件 Details 會從元素中顯示的 <input>People 元件接收資料 (Data) 。 當使用者選取其中 <input> 一個元素時,任何指定的顯示 <input> 元素都可以從使用者接收頁面的焦點。
    • 元件 People 會建立人員物件清單,以使用 Details 元件顯示。 每隔三秒,就會將新人員新增至集合。
    • 此示範可讓您:

    • 從數個 <input> 轉譯的 Details 元件中選取 。
    • 隨著人員集合自動成長,研究頁面焦點的行為。
    • Shared/Details.razor:

      <input value="@Data" /> @code { [Parameter] public string? Data { get; set; }

      在下列 People 元件中 OnTimerCallback ,新增人員的每個反復專案都會 Blazor 重建整個集合。 頁面的焦點會維持在元素的 <input>相同索引位置上,因此每次新增人員時焦點都會轉移。 將焦點從使用者選取的行為移開。 使用下列元件示範不良行為之後, @key 指示詞屬性會用來改善使用者體驗。

      Pages/People.razor:

      @page "/people" @using System.Timers @implements IDisposable @foreach (var person in people) <Details Data="@person.Data" /> @code { private Timer timer = new Timer(3000); public List<Person> people = new() { new Person { Data = "Person 1" } }, { new Person { Data = "Person 2" } }, { new Person { Data = "Person 3" } } protected override void OnInitialized() timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => people.Insert(0, new Person Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}" StateHasChanged(); public void Dispose() => timer.Dispose(); public class Person public string? Data { get; set; }

      集合的內容 people 會隨著插入、刪除或重新排序的專案而變更。 重新呈現可能會導致可見的行為差異。 每次將人員插入集合中 people 時,目前焦點元素的 上一個元素 會收到焦點。 使用者的焦點會遺失。

      專案或元件與集合的對應程式可以使用 指示詞屬性來控制 @key@key使用 可保證根據索引鍵的值保留專案或元件。 Details如果上述範例中的元件是以專案為索引鍵 person , Blazor 則忽略重新呈現尚未變更的 Details 元件。

      若要修改 People 元件以搭配集合使用 @key 指示詞屬性 people ,請將 <Details> 專案更新為下列專案:

      <Details @key="person" Data="@person.Data" />
      

      當集合變更時 people ,實例與 person 實例之間的 Details 關聯會保留。 Person在集合開頭插入 時,會在該對應位置插入一個新 Details 實例。 其他實例會保持不變。 因此,當使用者新增至集合時,使用者的焦點不會遺失。

      使用 指示詞屬性時 @key ,其他集合更新會顯示相同的行為:

    • 如果實例從集合中刪除,則只會從 UI 中移除對應的元件實例。 其他實例會保持不變。
    • 如果重新排序集合專案,則會在 UI 中保留並重新排序對應的元件實例。
    • 索引鍵是每個容器元素或元件的本機。 索引鍵不會在檔中全域進行比較。

      使用時機 @key

      一般而言,每當清單轉譯 (時使用 @key ,例如,區塊) 中 foreach 存在適當的值來定義 @key

      當物件未變更時,您也可以使用 @key 來保留專案或元件子樹,如下列範例所示。

      範例 1:

      <li @key="person">
          <input value="@person.Data" />
      

      範例 2:

      <div @key="person">
          @* other HTML elements *@
      

      person如果實例變更,則 @key 屬性指示詞會 Blazor 強制:

    • 捨棄整個 <li><div> 及其子系。
    • 使用新的元素和元件重建 UI 內的子樹。
    • 這很適合保證集合在子樹內變更時不會保留任何 UI 狀態。

      的範圍 @key

      @key屬性指示詞的範圍設定為其父系內自己的同層級。

      請思考一下下列範例。 和 firstsecond 索引鍵會在外部 <div> 元素的相同範圍內彼此進行比較:

      <div @key="first">...</div> <div @key="second">...</div>

      下列範例示範 first 自己的範圍和 second 索引鍵,彼此無關,且不會影響彼此。 每個 @key 範圍只會套用至其父 <div> 元素,而不是跨父 <div> 元素:

      <div @key="first">...</div> <div @key="second">...</div>

      Details針對稍早顯示的元件,下列範例會在相同的 @key 範圍內轉 person 譯資料,並示範 的 @key 一般使用案例:

      @foreach (var person in people) <Details @key="person" Data="@person.Data" />
      @foreach (var person in people)
          <div @key="person">
              <Details Data="@person.Data" />
          @foreach (var person in people)
              <li @key="person">
                  <Details Data="@person.Data" />
      

      下列範例只會範圍 @key 限定在每個 <div> 元件實例的 Details<li> 元素。 因此, person 集合中每個成員 people 的資料不會在轉譯 Details 的元件上的每個 person 實例上索引鍵。 使用 @key 時,請避免下列模式:

      @foreach (var person in people)
              <Details @key="person" Data="@person.Data" />
          @foreach (var person in people)
                  <Details @key="person" Data="@person.Data" />
      

      不使用時機 @key

      使用 @key 轉譯時會有效能成本。 效能成本並不大,但只指定 @key 是否保留專案或元件會讓應用程式受益。

      即使 @key 未使用, Blazor 仍會盡可能保留子專案和元件實例。 唯一使用 @key 的優點是控制模型實例 如何 對應至保留的元件實例,而不是 Blazor 選取對應。

      要用於 的值 @key

      一般而言,為 提供下列其中一個值 @key 是有意義的:

    • 模型物件實例。 例如, Person 在先前的範例中使用實例 (person) 。 這可確保根據物件參考相等進行保留。
    • 唯一識別碼。 例如,唯一識別碼可以根據 、 stringGuid 類型的 int 主鍵值。
    • 請確定用於 @key 的值不會衝突。 如果在相同的父元素內偵測到衝突的值,則會擲回例外狀況, Blazor 因為它無法決定性地將舊元素或元件對應至新的元素或元件。 只使用相異值,例如物件實例或主鍵值。

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

      @page "/"
      @attribute [Authorize]
      

      條件式 HTML 元素屬性

      HTML 元素屬性屬性會根據 .NET 值有條件地設定。 如果值為 falsenull ,則未設定 屬性。 如果值為 true ,則會設定 屬性。

      在下列範例中, IsCompleted 判斷是否已設定專案的 <input>checked 屬性。

      Pages/ConditionalAttribute.razor:

      @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; }

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

      某些 HTML 屬性,例如 aria-pressed ,在 .NET 類型為 bool 時無法正常運作。 在這些情況下,請使用 string 類型,而不是 bool

      原始 HTML

      字串通常會使用 DOM 文位元組點來轉譯,這表示任何可能包含的標記都會被忽略並視為常值文字。 若要轉譯原始 HTML,請將 HTML 內容包裝在值中 MarkupString 。 此值會剖析為 HTML 或 SVG,並插入 DOM。

      轉譯從任何不受信任的來源建構的原始 HTML 是 安全性風險 ,應 一律 避免。

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

      Pages/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 範本

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

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

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

      Pages/RazorTemplate.razor:

      @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 規則支援 SVG 影像, (.css) :

      .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 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 xmlns="http://www.w3.org/2000/svg">
          <foreignObject>
              <Robot />
          </foreignObject>
      @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 。 若要停用此效能優化並保留空白字元,請採取下列其中一個動作:

    • @preservewhitespace true將 指示詞新增至檔案頂端 Razor (.razor) ,以將喜好設定套用至特定元件。
    • @preservewhitespace true 檔案內 _Imports.razor 新增 指示詞,以將喜好設定套用至子目錄或整個專案。
    • 在大部分情況下,不需要採取任何動作,因為應用程式通常會繼續正常運作 (,但) 更快。 如果等量空白字元會造成特定元件的轉譯問題,請使用 @preservewhitespace true 該元件來停用此優化。

      泛型型別參數支援

      指示 @typeparam 詞會宣告所產生元件類別的 泛型型別參數

      @typeparam TItem
      

      支援具有類型條件約束的 where C# 語法:

      @typeparam TEntity where TEntity : IEntity
      

      在下列範例中 ListGenericTypeItems1 ,元件一般類型為 TExample

      Shared/ListGenericTypeItems1.razor:

      @typeparam TExample @if (ExampleList is not null) @foreach (var item in ExampleList) <li>@item</li> @code { [Parameter] public IEnumerable<TExample>? ExampleList{ get; set; }

      下列 GenericTypeExample1 元件會轉譯兩 ListGenericTypeItems1 個元件:

    • 字串或整數資料會指派給 ExampleList 每個元件的 參數。
    • int針對每個元件的類型參數 () TExample ,會設定符合所指派資料類型的 型 string 別或 。
    • Pages/GenericTypeExample1.razor:

      @page "/generic-type-example-1" <h1>Generic Type Example 1</h1> <ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" TExample="string" /> <ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" TExample="int" />

      如需詳細資訊,請參閱Razor ASP.NET Core的語法參考。 如需使用樣板化元件進行泛型輸入的範例,請參閱ASP.NET Core Blazor 樣板化元件

      串聯泛型型別支援

      上階元件可以使用 屬性,依名稱將類型參數串聯至子系[CascadingTypeParameter]。 此屬性可讓泛型型別推斷自動搭配具有相同名稱之型別參數的子系使用指定的型別參數。

      藉由新增 @attribute [CascadingTypeParameter(...)] 至元件,指定的泛型型別引數會自動由子系使用::

    • 巢狀為相同 .razor 檔中元件的子內容。
    • 也宣告 @typeparam 具有相同名稱的 。
    • 請勿為類型參數明確提供或隱含推斷另一個值。 如果提供或推斷另一個值,則會優先于串聯泛型型別。
    • 收到串聯類型參數時,元件會從具有相符名稱之 最接近的上階 CascadingTypeParameterAttribute 取得參數值。 重迭泛型型別參數會在特定的子樹內覆寫。

      比對只會依名稱執行。 因此,我們建議避免具有泛型名稱的串聯泛型型別參數,例如 TTItem 。 如果開發人員選擇串連類型參數,則隱含表示其名稱是唯一的,足以與不相關的元件中的其他串聯類型參數衝突。

      在下列其中一種方法中,泛型型別可以串聯至子元件,其中一個方法具有上階 (父系) 元件,如下列兩個子區段所示:

    • 明確設定串聯泛型型別。
    • 推斷串聯泛型型別。
    • 下列小節提供使用下列兩 ListDisplay 個元件之上述方法的範例。 元件會接收和轉譯清單資料,而且通常類型為 TExample 。 這些元件僅供示範之用,且只與轉譯清單的文字色彩不同。 如果您想要在本機測試應用程式中的下列子區段中試驗元件,請先將下列兩個元件新增至應用程式。

      Shared/ListDisplay1.razor:

      @typeparam TExample
      @if (ExampleList is not null)
          <ul style="color:blue">
              @foreach (var item in ExampleList)
                  <li>@item</li>
      @code {
          [Parameter]
          public IEnumerable<TExample>? ExampleList { get; set; }
      

      Shared/ListDisplay2.razor:

      @typeparam TExample
      @if (ExampleList is not null)
          <ul style="color:red">
              @foreach (var item in ExampleList)
                  <li>@item</li>
      @code {
          [Parameter]
          public IEnumerable<TExample>? ExampleList { get; set; }
      

      以上階元件為基礎的明確泛型型別

      本節中的示範會針對 明確 TExample 重迭類型。

      本節使用Cascaded 泛型型別支援區段中的兩 ListDisplay 個元件。

      下列 ListGenericTypeItems2 元件會接收資料,並將名為 TExample 的泛型型別參數串聯至其子代元件。 在即將推出的父元件中 ListGenericTypeItems2 ,元件是用來顯示具有上述 ListDisplay 元件的清單資料。

      Shared/ListGenericTypeItems2.razor:

      @attribute [CascadingTypeParameter(nameof(TExample))]
      @typeparam TExample
      <h2>List Generic Type Items 2</h2>
      @ChildContent
      @code {
          [Parameter]
          public RenderFragment? ChildContent { get; set; }
      

      下列 GenericTypeExample2 父元件會設定兩 ListGenericTypeItems2 個元件的子內容 (RenderFragment) ,指定 ListGenericTypeItems2 () TExample 類型,這些類型會串聯至子元件。 ListDisplay 元件會轉譯為範例中顯示的清單專案資料。 字串資料會與第一個 ListGenericTypeItems2 元件搭配使用,而整數資料則與第二個 ListGenericTypeItems2 元件搭配使用。

      Pages/GenericTypeExample2.razor:

      @page "/generic-type-example-2"
      <h1>Generic Type Example 2</h1>
      <ListGenericTypeItems2 TExample="string">
          <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
          <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
      </ListGenericTypeItems2>
      <ListGenericTypeItems2 TExample="int">
          <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
          <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
      </ListGenericTypeItems2>
      

      明確指定類型也允許使用 級聯值和參數 將資料提供給子元件,如下列示範所示。

      Shared/ListDisplay3.razor:

      @typeparam TExample
      @if (ExampleList is not null)
          <ul style="color:blue">
              @foreach (var item in ExampleList)
                  <li>@item</li>
      @code {
          [CascadingParameter]
          protected IEnumerable<TExample>? ExampleList { get; set; }
      

      Shared/ListDisplay4.razor:

      @typeparam TExample
      @if (ExampleList is not null)
          <ul style="color:red">
              @foreach (var item in ExampleList)
                  <li>@item</li>
      @code {
          [CascadingParameter]
          protected IEnumerable<TExample>? ExampleList { get; set; }
      

      Shared/ListGenericTypeItems3.razor:

      @attribute [CascadingTypeParameter(nameof(TExample))]
      @typeparam TExample
      <h2>List Generic Type Items 3</h2>
      @ChildContent
      @if (ExampleList is not null)
          <ul style="color:green">
              @foreach(var item in ExampleList)
                  <li>@item</li>
              Type of <code>TExample</code>: @typeof(TExample)
      @code {
          [CascadingParameter]
          protected IEnumerable<TExample>? ExampleList { get; set; }
          [Parameter]
          public RenderFragment? ChildContent { get; set; }
      

      在下列範例中串連資料時,必須將類型提供給 ListGenericTypeItems3 元件。

      Pages/GenericTypeExample3.razor:

      @page "/generic-type-example-3"
      <h1>Generic Type Example 3</h1>
      <CascadingValue Value="@stringData">
          <ListGenericTypeItems3 TExample="string">
              <ListDisplay3 />
              <ListDisplay4 />
          </ListGenericTypeItems3>
      </CascadingValue>
      <CascadingValue Value="@integerData">
          <ListGenericTypeItems3 TExample="int">
              <ListDisplay3 />
              <ListDisplay4 />
          </ListGenericTypeItems3>
      </CascadingValue>
      @code {
          private List<string> stringData = new() { "Item 1", "Item 2" };
          private List<int> integerData = new() { 1, 2, 3 };
      

      當多個泛型型別重迭時,必須傳遞集合中所有泛型型別的值。 在下列範例中, TItemTValueTEditGridColumn 泛型型別,但放置 GridColumn 的父元件不會指定 TItem 類型:

      <GridColumn TValue="string" TEdit="@TextEdit" />
      

      上述範例會產生編譯時期錯誤,指出 GridColumn 元件遺漏 TItem 類型參數。 有效的程式碼會指定所有類型:

      <GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" />
      

      根據上階元件推斷泛型型別

      本節中的示範會串聯為 推斷的類型 TExample

      本節使用Cascaded 泛型型別支援區段中的兩 ListDisplay 個元件。

      Shared/ListGenericTypeItems4.razor:

      @attribute [CascadingTypeParameter(nameof(TExample))]
      @typeparam TExample
      <h2>List Generic Type Items 4</h2>
      @ChildContent
      @if (ExampleList is not null)
          <ul style="color:green">
              @foreach(var item in ExampleList)
                  <li>@item</li>
              Type of <code>TExample</code>: @typeof(TExample)
      @code {
          [Parameter]
          public IEnumerable<TExample>? ExampleList { get; set; }
          [Parameter]
          public RenderFragment? ChildContent { get; set; }
      

      下列 GenericTypeExample4 具有推斷串聯類型的元件會提供不同的資料來顯示。

      Pages/GenericTypeExample4.razor:

      @page "/generic-type-example-4"
      <h1>Generic Type Example 4</h1>
      <ListGenericTypeItems4 ExampleList="@(new List<string> { "Item 5", "Item 6" })">
          <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
          <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
      </ListGenericTypeItems4>
      <ListGenericTypeItems4 ExampleList="@(new List<int> { 7, 8, 9 })">
          <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
          <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
      </ListGenericTypeItems4>
      

      GenericTypeExample5下列具有推斷串聯類型的元件會提供相同的資料來顯示。 下列範例會將資料直接指派給元件。

      Pages/GenericTypeExample5.razor:

      @page "/generic-type-example-5"
      <h1>Generic Type Example 5</h1>
      <ListGenericTypeItems4 ExampleList="@stringData">
          <ListDisplay1 ExampleList="@stringData" />
          <ListDisplay2 ExampleList="@stringData" />
      </ListGenericTypeItems4>
      <ListGenericTypeItems4 ExampleList="@integerData">
          <ListDisplay1 ExampleList="@integerData" />
          <ListDisplay2 ExampleList="@integerData" />
      </ListGenericTypeItems4>
      @code {
          private List<string> stringData = new() { "Item 1", "Item 2" };
          private List<int> integerData = new() { 1, 2, 3 };
      

      從 JavaScript 轉譯 Razor 元件

      Razor元件可以從現有 JS 應用程式的 JavaScript () JS 動態轉譯。

      若要從 JS 轉譯 Razor 元件,請將元件註冊為要轉譯的根元件 JS ,並指派識別碼給元件:

    • Blazor Server在應用程式中,修改 中的 Program.cs 呼叫 AddServerSideBlazor

      builder.Services.AddServerSideBlazor(options =>
          options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
      

      上述程式碼範例需要應用程式的元件命名空間 (,例如, using BlazorSample.Pages;) 檔案中的 Program.cs 元件。

    • Blazor WebAssembly在應用程式中,于 中 Program.cs 呼叫 RootComponentsRegisterForJavaScript

      builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
      

      上述程式碼範例需要應用程式的元件命名空間 (,例如, using BlazorSample.Pages;) 檔案中的 Program.cs 元件。

      載入 Blazor 應用程式 JS (blazor.server.jsblazor.webassembly.js) 。 使用已註冊的識別碼將元件轉 JS 譯為容器元素,視需要傳遞元件參數:

      let containerElement = document.getElementById('my-counter');
      await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });
      

      Blazor 自訂元素

      實驗性支援可用來使用Microsoft.AspNetCore.Components.CustomElements NuGet 套件建置自訂專案。 自訂元素使用標準 HTML 介面來實作自訂 HTML 元素。

      實驗性功能是為了探索功能可行性而提供,而且可能無法以穩定版本提供。

      將根元件註冊為自訂專案:

    • Blazor Server在應用程式中,修改 中的 Program.cs 呼叫 AddServerSideBlazor

      builder.Services.AddServerSideBlazor(options =>
          options.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
      

      上述程式碼範例需要應用程式的元件命名空間 (,例如, using BlazorSample.Pages;) 檔案中的 Program.cs 元件。

    • Blazor WebAssembly在應用程式中,于 中 Program.cs 呼叫 RootComponentsRegisterAsCustomElement

      builder.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
      

      上述程式碼範例需要應用程式的元件命名空間 (,例如, using BlazorSample.Pages;) 檔案中的 Program.cs 元件。

      在腳本標籤之前Blazor ,在應用程式的 HTML 中包含下列 <script> 標記:

      <script src="/_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
      

      搭配任何 Web 架構使用自訂專案。 例如,上述計數器自訂元素用於具有下列標記的React應用程式中:

      <my-counter increment-amount={incrementAmount}></my-counter>
      

      如需如何使用 建立自訂專案 Blazor 的完整範例,請參閱Blazor 自訂元素範例專案

      自訂元素功能目前為 實驗性、不支援,且隨時可能變更或移除。 歡迎您對這種特定方法符合您需求的意見反應。

      產生Angular和React元件

      從 Razor Web 架構的元件產生架構特定的 JavaScript (JS) 元件,例如Angular或React。 這項功能未隨附于 .NET 6 中,但由從 JS 轉譯元件的新支援 Razor 啟用。 JS GitHub 上的元件產生範例示範如何從 Razor 元件產生Angular和React元件。 如需詳細資訊,請參閱 GitHub 範例應用程式的 README.md 檔案。

      Angular和React元件功能目前為實驗性、不支援,且隨時可能變更或移除。 歡迎您對這種特定方法符合您需求的意見反應。

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

      元件是使用 C# 和 HTML 標籤的組合,在副檔名 .razor 為的元件檔案中 Razor 實作。

      Razor 語法

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

    • 指示詞:變更剖析元件標記或函式的方式。 例如, @page 指示詞會指定具有路由範本的可路由元件,而且可以直接由使用者在特定 URL 的瀏覽器中要求來連線。
    • 指示詞屬性:變更剖析元件專案或函式的方式。 例如,元素的 <input> 指示詞屬性會將 @bind 資料系結至元素的值。
    • 本文和檔集的其他文章 Blazor 會進一步說明元件中使用的指示詞和指示詞屬性。 如需語法的 Razor 一般資訊,請參閱Razor ASP.NET Core的語法參考

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

    • ProductDetail.razor 有效。
    • productDetail.razor 無效。
    • 檔中使用的 Blazor 常見 Blazor 命名慣例包括:

    • 元件檔案路徑會使用 Pascal 案例†並出現在顯示元件程式碼範例之前。 路徑表示典型的資料夾位置。 例如, Pages/ProductDetail.razor 表示 ProductDetail 元件具有 的 ProductDetail.razor 檔案名,且位於 Pages 應用程式的 資料夾中。
    • 可路由傳送元件的元件檔案路徑會與其 URL 相符,並顯示元件路由範本中單字之間的空格。 例如, ProductDetail 在相對 URL /product-detail 的瀏覽器中要求具有路由範本 (/product-detail@page "/product-detail") 的元件。
    • †Pascal 大小寫 (大寫) 是不含空格和標點符號的命名慣例,且每個字的第一個字母大寫,包括第一個字。

      藉由提供路由範本給應用程式中每個可存取的元件以及 指示 @page 詞,即可達成中的 Blazor 路由。 Razor編譯具有 @page 指示詞的檔案時,產生的類別會指定 RouteAttribute 路由範本。 在執行時間,路由器會搜尋具有 的 RouteAttribute 元件類別,並轉譯任何元件具有符合所要求 URL 的路由範本。

      下列 HelloWorld 元件使用 的 /hello-world 路由範本。 元件的轉譯網頁會到達相對 URL /hello-world 。 使用預設通訊協定、主機和埠在本機執行 Blazor 應用程式時,會在 HelloWorld 瀏覽器中 https://localhost:5001/hello-world 要求元件。 產生網頁的元件通常位於 Pages 資料夾中,但您可以使用任何資料夾來保存元件,包括巢狀資料夾內。

      Pages/HelloWorld.razor:

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

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

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

      元件的 UI 是使用Razor 語法來定義,其中包含 Razor 標記、C# 和 HTML。 編譯應用程式時,HTML 標籤和 C# 轉譯邏輯會轉換成元件類別。 產生的類別名稱符合檔案名。

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

    • 屬性和欄位初始化運算式。
    • 父元件和路由參數所傳遞引數的參數值。
    • 使用者事件處理、生命週期事件和自訂群組件邏輯的方法。
    • 元件成員用於使用以符號開頭的 @ C# 運算式來轉譯邏輯。 例如,C# 欄位會以功能變數名稱前置 @ 詞呈現。 下列 Markup 元件會評估並轉譯:

    • headingFontStyle 表示標題專案的 CSS 屬性值 font-style
    • headingText 為標題專案的內容。
    • Pages/Markup.razor:

      @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# 類別 ,而且可以在專案內的任何位置放置。 產生網頁的元件通常位於 Pages 資料夾中。 非頁面元件通常會放在 Shared 資料夾或新增至專案的自訂資料夾。

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

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

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

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

      請考慮下列 Heading 元件,可供其他元件用來顯示標題。

      Shared/Heading.razor:

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

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

      Pages/HeadingExample.razor:

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

      如果元件包含一個 HTML 元素,其大寫第一個字母不符合相同命名空間內的元件名稱,就會發出警告,指出元素有非預期的名稱。 @using新增元件的命名空間指示詞可讓元件使用,以解析警告。 如需詳細資訊,請參閱 命名空間一 節。

      Heading本節中顯示的元件範例沒有 @page 指示詞,因此 Heading 元件無法透過瀏覽器中的直接要求直接存取使用者。 不過,任何具有 指示詞的元件都可以在另一個 @page 元件中巢狀。 Heading如果直接存取元件的方式是在其檔案頂端 Razor 包含 @page "/heading" ,則會針對 和 /heading-example/heading 瀏覽器要求轉譯元件。

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

    • 元件的 Counter 命名空間為 BlazorSample.Pages
    • 元件的完整型別名稱為 BlazorSample.Pages.Counter
    • 對於保存元件的自訂資料夾,請將 指示詞新增 @using 至父元件或應用程式檔案 _Imports.razor 。 下列範例會讓資料夾中的 Components 元件可供使用:

      @using BlazorSample.Components
      

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

      元件也可以使用其完整名稱來參考,而不需要 @using 指示詞。 下列範例會直接參考 ProductDetail 應用程式資料夾中的元件 Components

      <BlazorSample.Components.ProductDetail />
      

      所撰寫 Razor 元件的命名空間是以下列優先順序 (為基礎) :

    • @namespace例如, @namespace BlazorSample.CustomNamespace 檔案標記中的 Razor 指示詞 () 。
    • 例如,專案檔中的專案 RootNamespace () <RootNamespace>BlazorSample</RootNamespace>
    • 專案名稱,取自專案檔的檔案名 () .csproj ,以及專案根目錄到元件的路徑。 例如,架構會 {PROJECT ROOT}/Pages/Index.razor 使用 () BlazorSample.csproj 的專案命名空間 BlazorSample 解析為元件的命名空間 BlazorSample.PagesIndex{PROJECT ROOT} 是專案根路徑。 元件遵循 C# 名稱系結規則。 Index在此範例中的元件中,範圍中的元件都是所有元件:
      • 在相同的資料夾中, Pages
      • 專案根目錄中未明確指定不同命名空間的元件。
      • 不支援下列各項:

      • 資格 global::
      • 使用別名語句匯入 using 元件。 例如,不支援 @using Foo = Bar
      • 部分限定名稱。 例如,您無法新增 @using BlazorSample 至元件,然後使用 參考 NavMenu 應用程式 Shared 資料夾中的元件 (Shared/NavMenu.razor) <Shared.NavMenu></Shared.NavMenu>
      • 部分類別支援

        元件會產生為 C# 部分類別 ,並使用下列其中一種方法來撰寫:

      • 單一檔案包含一或多個 @code 區塊、HTML 標籤和 Razor 標記中定義的 C# 程式碼。 Blazor 專案範本會使用此單一檔案方法來定義其元件。
      • HTML 和 Razor 標記會放在 Razor 檔案 (.razor) 中。 C# 程式碼會放在定義為部分類別的程式碼後置檔案中, (.cs) 。
      • 定義元件特定樣式的元件樣式表單是個別的檔案 (.css) 。 BlazorCSS 隔離稍後會在ASP.NET CORE Blazor CSS 隔離中說明。

        下列範例顯示預設 Counter 元件,其中包含 @code 從 Blazor 專案範本產生之應用程式中的區塊。 標記和 C# 程式碼位於相同的檔案中。 這是元件撰寫中最常見的方法。

        Pages/Counter.razor:

        @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 標記:

        Pages/CounterPartialClass.razor:

        @page "/counter-partial-class"
        <h1>Counter</h1>
        <p>Current count: @currentCount</p>
        <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
        

        Pages/CounterPartialClass.razor.cs:

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

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

        元件所使用的一般命名空間:

        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;
        

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

        using BlazorSample;
        using BlazorSample.Shared;
        

        指示 @inherits 詞是用來指定元件的基類。 下列範例示範元件如何繼承基類,以提供元件的屬性和方法。 基 BlazorRocksBase 類衍生自 ComponentBase

        Pages/BlazorRocks.razor:

        @page "/blazor-rocks" @inherits BlazorRocksBase <h1>@BlazorRocksText</h1>

        BlazorRocksBase.cs:

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

        元件參數會將資料傳遞至元件,並使用具有 屬性之元件類別[Parameter]上的公用C# 屬性來定義。 在下列範例中,內建的參考類型 () System.String 和使用者定義的參考類型 (PanelBody) 會傳遞為元件參數。

        PanelBody.cs:

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

        Shared/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 @code { [Parameter] public string Title { get; set; } = "Set By Child"; [Parameter] public PanelBody Body { get; set; } = new() Text = "Set by child.", Style = "normal"

        支援提供元件參數的初始值,但不會建立在第一次轉譯元件之後寫入其自有參數的元件。 如需詳細資訊,請參閱本文的 覆寫參數 一節。

        元件的 TitleBody 元件參數 ParameterChild 是由 HTML 標籤中的引數所設定,可轉譯元件的實例。 下列 ParameterParent 元件會轉譯兩 ParameterChild 個元件:

      • 第一個 ParameterChild 元件會轉譯,而不提供參數引數。
      • 第二 ParameterChild 個元件會接收 和 BodyParameterParent 元件的值 Title ,其會使用明確的 C# 運算式來設定 屬性的值 PanelBody
      • Pages/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 元件的預設值。

        為了清楚起見,轉譯的 CSS 樣式類別不會顯示在下列轉譯的 HTML 標籤中。

        <h1>Child component (without attribute values)</h1>
            <div>Set By Child</div>
            <div>Set by child.</div>
        <h1>Child component (with attribute values)</h1>
            <div>Set by Parent</div>
            <div>Set by parent.</div>
        

        使用Razor 保留 @ 符號,將方法的 C# 欄位、屬性或結果指派給元件參數做為 HTML 屬性值。 下列 ParameterParent2 元件會顯示上述 ParameterChild 元件的四個實例,並將其參數值設定 Title 為:

      • 欄位的值 title
      • C# 方法的結果 GetTitle
      • 目前格式為 long 格式 ToLongDateString 的本機日期,其使用 隱含 C# 運算式
      • 物件的 panelDataTitle 屬性。
      • 字串 @ 參數需要前置詞。 否則,架構會假設已設定字串常值。

        在字串參數之外,我們建議將前置詞用於 @ 非精簡字元,即使它們並非嚴格必要也一樣。

        我們 @ 不建議使用常值前置詞 (例如布林值) 、關鍵字 (例如、 this) 或 null ,但您可以選擇使用它們。 例如, IsFixed="@true" 並不常見,但受到支援。

        在大部分情況下,每個 HTML5 規格的參數屬性值引號都是選擇性的。 例如, Value=this 支援 ,而不是 Value="this" 。 不過,我們建議使用引號,因為更容易記住並廣泛採用 Web 技術。

        在整個檔中,程式碼範例:

      • 一律使用引號。 範例: Value="this".
      • 非音元一律使用 @ 前置詞,即使它是選擇性的。 範例: Title="@title" ,其中 title 是字串類型的變數。 Count="@ct",其中 ct 是數位型別變數。
      • 運算式外部的 Razor 常值一律會避免 @ 。 範例: IsFixed="true".
      • Pages/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";

        將 C# 成員指派給元件參數時,請在成員前面加上 @ 符號,且永遠不會在參數的 HTML 屬性前面加上前置詞。

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

        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 」 與物件的屬性值串連。 雖然頁面 () .cshtml 支援 Razor 此語法,但對元件中子參數的 Title 指派無效。 不支援下列 Razor 語法:

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

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

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

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

        Pages/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";

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

        支援提供元件參數的初始值,但不會建立在第一次轉譯元件之後寫入其自有參數的元件。 如需詳細資訊,請參閱本文的 覆寫參數 一節。

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

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

        請勿將自訂邏輯放在 或 set 存取子中 get ,因為元件參數只是用來作為父元件的通道,以便將資訊流向子元件。 set如果子元件屬性的存取子包含導致重新呈現父元件的邏輯,則會產生無限轉譯迴圈結果。

        若要轉換接收的參數值:

      • 將參數屬性保留為自動屬性,以代表提供的原始資料。
      • 建立不同的屬性或方法,根據參數屬性提供轉換的資料。
      • 覆寫 OnParametersSetAsync 以在每次收到新資料時轉換已接收的參數。

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

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

        在 初始指派 DateTime.Now 之後, 請勿 在開發人員程式碼中指派值 StartData 。 如需詳細資訊,請參閱本文的 覆寫參數 一節。

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

        支援選擇性路由參數。 在下列範例中 text ,選擇性參數會將路由區段的值指派給元件的 Text 屬性。 如果區段不存在,生命週期 Text 方法中的OnInitialized值會設定為 「 fantastic 」。

        Pages/RouteParameter.razor:

        @page "/route-parameter/{text?}" <h1>Blazor is @Text!</h1> @code { [Parameter] public string Text { get; set; } protected override void OnInitialized() Text = Text ?? "fantastic";

        如需擷取所有路由參數 () {*pageRoute} 的相關資訊,可擷取跨多個資料夾界限的路徑,請參閱ASP.NET Core Blazor 路由和流覽

        子內容轉譯片段

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

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

        Shared/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> @code { [Parameter] public RenderFragment ChildContent { get; set; }

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

        不支援 RenderFragment事件回呼

        下列 RenderFragmentParent 元件提供轉譯的內容,方法是將內容 RenderFragmentChild 放在子元件的開頭和結束記號內。

        Pages/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 轉譯元件需要局部索引變數。 下列範例可以新增至上述 RenderFragmentParent 元件:

        <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 迴圈。 下列範例可以新增至上述 RenderFragmentParent 元件:

        <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 佈局
      • 跨元件階層傳遞資料
      • 樣板化元件
      • 全域例外狀況處理
      • Blazor 架構的 內 Razor 建元件ChildContent 使用相同的元件參數慣例來設定其內容。 您可以在 API 檔中搜尋元件參數屬性名稱 ChildContent 來查看設定子內容的元件 , (篩選 API 的搜尋字詞 「ChildContent」)

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

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

        <h1>Hello, world!</h1>
        @RenderWelcomeInfo
        <p>Render the welcome info a second time:</p>
        @RenderWelcomeInfo
        @code {
            private RenderFragment RenderWelcomeInfo = __builder =>
                <p>Welcome to your new app!</p>
        

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

        覆寫的參數

        架構 Blazor 通常會強制安全父對子參數指派:

      • 參數不會意外覆寫。
      • 副作用最小化。 例如,會避免其他轉譯,因為它們可能會建立無限的轉譯迴圈。
      • 子元件會收到新的參數值,這些值可能在父元件重新呈現時覆寫現有的值。 使用一或多個資料系結參數開發元件時,通常會意外覆寫子元件中的參數值,而開發人員會直接寫入子系中的參數:

      • 子元件會從父元件轉譯一或多個參數值。
      • 子系會直接寫入參數的值。
      • 父元件會重新呈現並覆寫子參數的值。
      • 覆寫參數值的可能性也會延伸至子元件的屬性 set 存取子。

        我們的一般指引不是在第一次轉譯元件之後,建立直接寫入其自有參數的元件。

        請考慮下列 Expander 元件:

      • 轉譯子內容。
      • 切換顯示子內容與元件參數 (Expanded) 。
      • 在下列 Expander 元件示範覆寫的參數之後,會顯示已 Expander 修改的元件,以示範此案例的正確方法。 下列範例可以放在本機範例應用程式中,以體驗所述的行為。

        Shared/Expander.razor:

        <div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem"> <div class="card-body"> <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2> @if (Expanded) <p class="card-text">@ChildContent</p> @code { [Parameter] public bool Expanded { private get; set; } [Parameter] public RenderFragment ChildContent { get; set; } private void Toggle() Expanded = !Expanded;

        元件 Expander 會新增至下列可能呼叫 StateHasChangedExpanderExample 父元件:

      • 在開發人員程式碼中呼叫 StateHasChanged 會通知元件其狀態已變更,而且通常會觸發元件重新呈現以更新 UI。 StateHasChanged稍後會在ASP.NET Core Razor 元件生命週期ASP.NET Core Razor 元件轉譯中詳細說明。
      • 按鈕的 @onclick 指示詞屬性會將事件處理常式附加至按鈕的 onclick 事件。 事件處理會在稍後ASP.NET Core Blazor 事件處理中詳細說明。
      • Pages/ExpanderExample.razor:

        @page "/expander-example" <Expander Expanded="true"> Expander 1 content </Expander> <Expander Expanded="true" /> <button @onclick="StateHasChanged"> Call StateHasChanged </button>

        一開始, Expander 元件會在切換其 Expanded 屬性時獨立運作。 子元件會如預期般維持其狀態。

        如果在 StateHasChanged 父元件中呼叫 ,則如果架構的參數可能已變更,架構 Blazor 會重新呈現子元件:

      • 針對明確檢查的參數類型 Blazor 群組,如果子元件偵測到任何參數已變更, Blazor 請重新呈現子元件。
      • 針對未核取的參數類型,不論參數是否已變更, Blazor 都會重新呈現子元件。 子內容屬於這個參數類型類別,因為子內容的類型為 RenderFragment ,這是參考其他可變動物件的委派。
      • ExpanderExample針對元件:

      • 第一個 Expander 元件會在可能變動 RenderFragment 的 中設定子內容,因此父元件中的 呼叫 StateHasChanged 會自動重新呈現元件,並可能會覆寫 Expanded 的值至其初始化值 true
      • 第二 Expander 個元件不會設定子內容。 因此,可能可變 RenderFragment 的 不存在。 父元件中的 呼叫 StateHasChanged 不會自動重新呈現子元件,因此不會覆寫元件的 Expanded 值。
      • 若要在上述案例中維護狀態,請使用元件中的 Expander私用欄位來維護其切換狀態。

        下列修訂的 Expander 元件:

      • 接受父代的 Expanded 元件參數值。
      • 將元件參數值指派給事件中的OnInitialized私用欄位 (expanded) 。
      • 使用私用欄位來維護其內部切換狀態,示範如何避免直接寫入參數。
      • 本節中的建議會延伸至元件參數 set 存取子中的類似邏輯,這可能會導致類似的不想要副作用。

        Shared/Expander.razor:

        <div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem"> <div class="card-body"> <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2> @if (expanded) <p class="card-text">@ChildContent</p> @code { private bool expanded; [Parameter] public bool Expanded { private get; set; } [Parameter] public RenderFragment ChildContent { get; set; } protected override void OnInitialized() expanded = Expanded; private void Toggle() expanded = !expanded;

        如需詳細資訊,請參閱Blazor dotnet/aspnetcore #24599) (雙向系結錯誤

        如需變更偵測的詳細資訊,請參閱 BlazorASP.NET Core Razor 元件轉譯

        屬性展開和任意參數

        元件除了元件的宣告參數之外,還可以擷取和轉譯其他屬性。 您可以在字典中擷取其他屬性,然後在使用 @attributesRazor 指示詞屬性轉譯元件時,在元素上擷取。 此案例適用于定義產生支援各種自訂之標記專案的元件。 例如,為支援許多參數的 個別 <input> 定義屬性可能很麻煩。

        在下列 Splat 元件中:

      • 第一個 <input> 元素 (id="useIndividualParams") 使用個別元件參數。
      • 第二 <input> 個元素 () id="useAttributesDict" 使用屬性 splatting。
      • Pages/Splat.razor:

        @page "/splat" <input id="useIndividualParams" maxlength="@maxlength" placeholder="@placeholder" required="@required" size="@size" /> <input id="useAttributesDict" @attributes="InputAttributes" /> @code { private string maxlength = "10"; private string placeholder = "Input placeholder text"; private string required = "required"; private string size = "50"; private Dictionary<string, object> InputAttributes { get; set; } = new() { "maxlength", "10" }, { "placeholder", "Input placeholder text" }, { "required", "required" }, { "size", "50" }

        網頁中的轉譯 <input> 元素完全相同:

        <input id="useIndividualParams"
               maxlength="10"
               placeholder="Input placeholder text"
               required="required"
               size="50">
        <input id="useAttributesDict"
               maxlength="10"
               placeholder="Input placeholder text"
               required="required"
               size="50">
        

        若要接受任意屬性,請定義 元件參數 ,並將 CaptureUnmatchedValues 屬性設定為 true

        @code {
            [Parameter(CaptureUnmatchedValues = true)]
            public Dictionary<string, object> InputAttributes { get; set; }
        

        上的 CaptureUnmatchedValues[Parameter] 屬性可讓 參數符合不符合任何其他參數的所有屬性。 元件只能使用 CaptureUnmatchedValues 定義單一參數。 與 搭配 CaptureUnmatchedValues 使用的屬性類型必須具有字串索引鍵的可指派。 Dictionary<string, object> IEnumerable<KeyValuePair<string, object>>在此案例中使用 或 IReadOnlyDictionary<string, object> 也是選項。

        相對於專案屬性位置的位置 @attributes 而言,的位置很重要。 在 元素上進行擷取時 @attributes ,屬性會從右至左處理 (最後一個) 。 請考慮使用子元件的父元件範例如下:

        Shared/AttributeOrderChild1.razor:

        <div @attributes="AdditionalAttributes" extra="5" /> @code { [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> AdditionalAttributes { get; set; }

        Pages/AttributeOrderParent1.razor:

        @page "/attribute-order-parent-1" <AttributeOrderChild1 extra="10" />

        extra元件的 AttributeOrderChild1 屬性設定為 的 @attributes 右邊。 當傳遞其他屬性時,元件的 AttributeOrderParent1<div> 譯會包含 extra="5" ,因為屬性會由右至左處理, (最後一個) :

        <div extra="5" />
        

        在下列範例中,和 @attributes 的順序 extra 會在子元件的 <div> 中反轉:

        Shared/AttributeOrderChild2.razor:

        <div extra="5" @attributes="AdditionalAttributes" /> @code { [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> AdditionalAttributes { get; set; }

        Pages/AttributeOrderParent2.razor:

        @page "/attribute-order-parent-2" <AttributeOrderChild2 extra="10" />

        <div>在父元件的轉譯網頁中,透過其他屬性傳遞時,會 extra="10" 包含 :

        <div extra="10" />
        

        擷取元件的參考

        元件參考提供方法來參考發出命令的元件實例。 若要擷取元件參考:

      • @ref將屬性新增至子元件。
      • 定義與子元件相同類型的欄位。
      • 轉譯元件時,欄位會填入元件實例。 然後,您可以在 實例上叫用 .NET 方法。

        請考慮下列 ReferenceChild 元件,以在呼叫訊息 ChildMethod 時記錄訊息。

        Shared/ReferenceChild.razor:

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

        只有在轉譯元件之後,才會填入元件參考,且其輸出包含 ReferenceChild 的 元素。 在轉譯元件之前,不會參考任何專案。

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

        若要搭配事件處理常式使用參考變數,請使用 Lambda 運算式,或在OnAfterRenderAsync 方法中 OnAfterRender指派事件處理常式委派。 這可確保在指派事件處理常式之前指派參考變數。

        下列 Lambda 方法會使用上述 ReferenceChild 元件。

        Pages/ReferenceParent1.razor:

        @page "/reference-parent-1" <button @onclick="@(() => childComponent.ChildMethod(5))"> Call <code>ReferenceChild.ChildMethod</code> with an argument of 5 </button> <ReferenceChild @ref="childComponent" /> @code { private ReferenceChild childComponent;

        下列委派方法會使用上述 ReferenceChild 元件。

        Pages/ReferenceParent2.razor:

        @page "/reference-parent-2" <button @onclick="callChildMethod"> Call <code>ReferenceChild.ChildMethod</code> with an argument of 5 </button> <ReferenceChild @ref="childComponent" /> @code { private ReferenceChild childComponent; private Action callChildMethod; protected override void OnAfterRender(bool firstRender) if (firstRender) callChildMethod = CallChildMethod; private void CallChildMethod() childComponent.ChildMethod(5);

        雖然擷取元件參考使用類似的語法來 擷取專案參考,但擷取元件參考不是 JavaScript Interop 功能。 元件參考不會傳遞至 JavaScript 程式碼。 元件參考僅適用于 .NET 程式碼。

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

        同步處理內容

        Blazor 會使用同步處理內容 (SynchronizationContext) 來強制執行單一邏輯執行緒的執行。 元件的 生命週期方法和 事件回呼會在 Blazor 同步處理內容上執行。

        Blazor Server的同步處理內容會嘗試模擬單一執行緒環境,使其與瀏覽器中的 WebAssembly 模型緊密相符,也就是單一執行緒。 在任何指定的時間點,工作只會在一個執行緒上執行,這會產生單一邏輯執行緒的印象。 沒有任何兩個作業同時執行。

        避免執行緒封鎖呼叫

        一般而言,請勿在元件中呼叫下列方法。 下列方法會封鎖執行執行緒,因而封鎖應用程式繼續工作,直到基礎 Task 完成為止:

      • Result
      • WaitAny
      • WaitAll
      • Sleep
      • GetResult
      • Blazor 使用本節所述的執行緒封鎖方法的檔範例只會使用方法進行示範,而不是建議的程式碼撰寫指引。 例如,一些元件程式碼示範會藉由呼叫 Thread.Sleep 來模擬長時間執行的進程。

        在外部叫用元件方法以更新狀態

        在事件中,元件必須根據外來事件更新,例如計時器或其他通知,請使用 InvokeAsync 方法,將程式碼執行分派至 Blazor 的同步處理內容。 例如,請考慮下列可通知任何接聽元件有關更新狀態的 通知服務Update您可以從應用程式中的任何位置呼叫 方法。

        TimerService.cs:

        using System; using System.Timers; using Microsoft.Extensions.Logging; public class TimerService : IDisposable private int elapsedCount; private readonly ILogger<TimerService> logger; private readonly NotifierService notifier; private Timer timer; public TimerService(NotifierService notifier, ILogger<TimerService> logger) this.notifier = notifier; this.logger = logger; public void Start() if (timer is null) timer = new(); timer.AutoReset = true; timer.Interval = 10000; timer.Elapsed += HandleTimer; timer.Enabled = true; logger.LogInformation("Started"); private async void HandleTimer(object source, ElapsedEventArgs e) elapsedCount += 1; await notifier.Update("elapsedCount", elapsedCount); logger.LogInformation($"elapsedCount: {elapsedCount}"); public void Dispose() timer?.Dispose();

        NotifierService.cs:

        using System; using System.Threading.Tasks; public class NotifierService public async Task Update(string key, int value) if (Notify != null) await Notify.Invoke(key, value); public event Func<string, int, Task> Notify;

        註冊服務:

      • Blazor WebAssembly在應用程式中,將服務註冊為 中的 Program.cs 單一服務:

        builder.Services.AddSingleton<NotifierService>();
        builder.Services.AddSingleton<TimerService>();
        
      • Blazor Server在應用程式中,將服務註冊為 中的 Startup.ConfigureServices 範圍:

        services.AddScoped<NotifierService>();
        services.AddScoped<TimerService>();
        

        NotifierService使用 來更新元件。

        Pages/ReceiveNotifications.razor:

        @page "/receive-notifications" @implements IDisposable @inject NotifierService Notifier @inject TimerService Timer <h1>Receive Notifications</h1> <h2>Timer Service</h2> <button @onclick="StartTimer">Start Timer</button> <h2>Notifications</h2> Status: @if (lastNotification.key is not null) <span>@lastNotification.key = @lastNotification.value</span> <span>Awaiting first notification</span> @code { private (string key, int value) lastNotification; protected override void OnInitialized() Notifier.Notify += OnNotify; public async Task OnNotify(string key, int value) await InvokeAsync(() => lastNotification = (key, value); StateHasChanged(); private void StartTimer() Timer.Start(); public void Dispose() Notifier.Notify -= OnNotify;

        在上述範例中:

      • NotifierService會在 同步處理內容之外 Blazor 叫用元件的 OnNotify 方法。 InvokeAsync 用來切換至正確的內容,並將轉譯排入佇列。 如需詳細資訊,請參閱ASP.NET Core Razor 元件轉譯
      • 元件會實作 IDisposableOnNotify委派在 方法中 Dispose 取消訂閱,在處置元件時由架構呼叫。 如需詳細資訊,請參閱ASP.NET Core Razor 元件生命週期
      • 用來 @key 控制專案和元件的保留

        轉譯專案或元件清單以及後續變更的專案或元件時,必須決定可以保留哪些先前的專案或元件, Blazor 以及模型物件應該如何對應至這些專案。 一般而言,此程式是自動的,而且可以忽略,但在某些情況下,您可能會想要控制進程。

        請考慮下列 DetailsPeople 元件:

      • 元件 Details 會從元素中顯示的 <input>People 元件接收 () 資料 Data 。 當使用者選取其中一個元素時,任何指定的顯示 <input> 專案都可以接收使用者頁面的 <input> 焦點。
      • 元件 People 會建立人員物件清單,以使用 Details 元件顯示。 每隔三秒,就會將新人員新增至集合。
      • 此示範可讓您:

      • 從數個 <input> 轉譯的 Details 元件中選取 。
      • 隨著人員集合自動成長,研究頁面焦點的行為。
      • Shared/Details.razor:

        <input value="@Data" /> @code { [Parameter] public string Data { get; set; }

        在下列 People 元件中,新增人員 OnTimerCallback 的每個反復專案都會 Blazor 重建整個集合。 頁面的焦點會保留在元素的 <input>相同索引位置上,因此每次新增人員時,焦點都會轉移。 將焦點移開使用者選取的不想要行為。 使用下列元件示範不佳的行為之後, @key 指示詞屬性會用來改善使用者體驗。

        Pages/People.razor:

        @page "/people" @using System.Timers @implements IDisposable @foreach (var person in people) <Details Data="@person.Data" /> @code { private Timer timer = new Timer(3000); public List<Person> people = new() { new Person { Data = "Person 1" } }, { new Person { Data = "Person 2" } }, { new Person { Data = "Person 3" } } protected override void OnInitialized() timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => people.Insert(0, new Person Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}" StateHasChanged(); public void Dispose() => timer.Dispose(); public class Person public string Data { get; set; }

        集合的內容 people 會隨著插入、刪除或重新排序的專案而變更。 重新呈現可能會導致可見的行為差異。 每次將人員插入集合中 people 時,目前焦點專案的 一個專案就會接收焦點。 使用者的焦點遺失。

        元素或元件與集合的對應程式可以使用 指示詞屬性來控制 @key@key使用 可確保根據索引鍵的值保留專案或元件。 Details如果上述範例中的元件是以 person 專案為索引鍵, Blazor 則會忽略重新呈現尚未變更的 Details 元件。

        若要修改 People 元件以搭配 people 集合使用 @key 指示詞屬性,請將 <Details> 元素更新為下列專案:

        <Details @key="person" Data="@person.Data" />
        

        people當集合變更時,會保留實例與 person 實例之間的 Details 關聯。 Person在集合開頭插入 時,會在該對應位置插入一個新 Details 實例。 其他實例會保持不變。 因此,當使用者新增至集合時,不會遺失使用者的焦點。

        使用 指示詞屬性時 @key ,其他集合更新會顯示相同的行為:

      • 如果從集合中刪除實例,則只會從 UI 中移除對應的元件實例。 其他實例會保持不變。
      • 如果重新排序集合專案,則會在 UI 中保留並重新排序對應的元件實例。
      • 索引鍵是每個容器元素或元件的本機。 索引鍵不會在檔中全域進行比較。

        使用時機 @key

        一般而言,每當清單轉譯 (時使用 @key ,例如,區塊) , foreach 且有適當的值可定義 @key

        當物件未變更時,您也可以使用 @key 來保留專案或元件子樹,如下列範例所示。

        範例 1:

        <li @key="person">
            <input value="@person.Data" />
        

        範例 2:

  •