共用方式為


預呈現 ASP.NET Core Razor 元件

註釋 / 注意

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

重要

此資訊涉及一款尚未發布的產品,在正式上市前可能會有重大修改。 Microsoft 對於此處提供的資訊,不做任何明確或隱含的保證。

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

本文解釋了伺服器渲染組件中的Razor組件預渲染方案。

預先呈現 是伺服器上初始轉譯頁面內容的程式,而不需要啟用轉譯控件的事件處理程式。 伺服器會盡快輸出頁面的 HTML UI,以回應初始要求,如此可改善應用程式對使用者的回應速度。 預渲染還可以通過渲染搜索引擎用於計算頁面級別的初始 HTTP 響應內容來改善搜尋引擎最佳化(SEO)。

保存預先轉譯的狀態

如果不保留預渲染的狀態,則預渲染過程中使用的狀態將丟失,並且在應用程序完全加載時必須重新創建。 如果任何狀態是非同步建立的,當元件重新渲染時,UI 可能會閃爍,因為預渲染的 UI 被取代。

請考慮以下 PrerenderedCounter1 計數器元件。 元件會在OnInitialized生命週期方法的預呈現階段設定初始隨機計數器值。 在與客戶端的SignalR連線建立後,元件將重新渲染,當OnInitialized第二次執行時,初始計數值會被取代。

PrerenderedCounter1.razor

@page "/prerendered-counter-1"
@rendermode @(new InteractiveServerRenderMode(prerender: true))
@inject ILogger<PrerenderedCounter1> Logger

<PageTitle>Prerendered Counter 1</PageTitle>

<h1>Prerendered Counter 1</h1>

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

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

@code {
    private int currentCount;

    protected override void OnInitialized()
    {
        currentCount = Random.Shared.Next(100);
        Logger.LogInformation("currentCount set to {Count}", currentCount);
    }

    private void IncrementCount() => currentCount++;
}

啟動應用程式並檢查元件的日誌記錄。 以下是範例輸出。

註釋 / 注意

如果應用程式採用 互動式路由 ,且頁面是透過內部 增強式導覽來連線,則不會發生預先呈現。 因此,您必須重新載入整個頁面,才能查看PrerenderedCounter1元件的以下輸出。 如需詳細資訊,請參閱 互動式路由和預先呈現 一節。

info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92

第一次記錄的計數發生在預渲染過程中。 當組件重新渲染後,計數會在預渲染後重新設置。 UI 在計數從 41 更新到 92 時也會閃爍。

若要在預先呈現期間保留計數器的初始值,Blazor 支援使用 PersistentComponentState 服務在預先呈現的頁面中持續保存狀態;對於內嵌於 Razor 頁面或 MVC 應用程式中的元件,則使用 保存元件狀態標籤協助程式

若要保留預先呈現的狀態,請使用 [SupplyParameterFromPersistentComponentState] 屬性將狀態保存在屬性中。 具有此屬性的屬性會在預先呈現期間使用 PersistentComponentState 服務自動保存。 當元件以互動方式轉譯或服務具現化時,就會擷取狀態。

根據預設,屬性會使用具有預設設定的 System.Text.Json 串行化程序進行串行化。 串行化不是安全修剪器,而且需要保留所使用的類型。 如需詳細資訊,請參閱設定 ASP.NET Core Blazor的修剪器

下列計數器元件會在預先呈現期間保存計數器狀態,並擷取狀態以初始化元件:

  • 屬性 [SupplyParameterFromPersistentComponentState] 會套用至 CounterState 類型 (State)。
  • 計數器的狀態會在 null 中被指定,當元件以互動方式渲染時會自動還原。

PrerenderedCounter2.razor

@page "/prerendered-counter-2"
@inject ILogger<PrerenderedCounter2> Logger

<PageTitle>Prerendered Counter 2</PageTitle>

<h1>Prerendered Counter 2</h1>

<p role="status">Current count: @State?.CurrentCount</p>

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

@code {
    [SupplyParameterFromPersistentComponentState]
    public CounterState? State { get; set; }

    protected override void OnInitialized()
    {
        if (State is null)
        {
            State = new() { CurrentCount = Random.Shared.Next(100) };
            Logger.LogInformation("CurrentCount set to {Count}", 
                State.CurrentCount);
        }
        else
        {
            Logger.LogInformation("CurrentCount restored to {Count}", 
                State.CurrentCount);
        }
    }

    private void IncrementCount()
    {
        if (State is not null)
        {
            State.CurrentCount++;
        }
    }

    public class CounterState
    {
        public int CurrentCount { get; set; }
    }
}

當元件執行時,CurrentCount 只在預渲染期間設定一次。 當元件重新渲染時,值將被恢復。 以下是範例輸出。

註釋 / 注意

如果應用程式採用 互動式路由 ,且頁面是透過內部 增強式導覽來連線,則不會發生預先呈現。 因此,您必須針對元件執行完整頁面重載,才能看到下列輸出。 如需詳細資訊,請參閱 互動式路由和預先呈現 一節。

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 96

在下列範例中,串行化相同類型的多個元件的狀態:

  • [SupplyParameterFromPersistentComponentState] 屬性標註的屬性會在預先呈現期間串行化和還原串行化。
  • 指示@key詞屬性是用來確保狀態與元件實例正確相關聯。
  • Element屬性在OnInitialized生命週期方法中初始化,以避免空參考例外,這類似於如何避免查詢參數和表單資料中的空參考。

PersistentChild.razor

<div>
    <p>Current count: @Element.CurrentCount</p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>

@code {
    [SupplyParameterFromPersistentComponentState]
    public State Element { get; set; }

    protected override void OnInitialized()
    {
        Element ??= new State();
    }

    private void IncrementCount()
    {
        Element.CurrentCount++;
    }

    private class State
    {
        public int CurrentCount { get; set; }
    }
}

Parent.razor

@page "/parent"

@foreach (var element in elements)
{
    <PersistentChild @key="element.Name" />
}

在下列範例中,用於序列化相依性注入服務的狀態:

  • [SupplyParameterFromPersistentComponentState] 屬性標註的屬性會在預渲染期間序列化,並在應用程式變成互動式時反序列化。
  • 方法AddPersistentService用於註冊服務以達到持久性。 因為無法從服務類型推斷轉譯模式,因此需要轉譯模式。 使用下列任何值:
    • RenderMode.Server:服務適用於互動式伺服器轉譯模式。
    • RenderMode.Webassembly:此服務適用於互動式 Webassembly 轉譯模式。
    • RenderMode.InteractiveAuto:如果元件在任一模式中轉譯,則服務適用於互動式伺服器和互動式Web元件轉譯模式。
  • 服務將會在互動式渲染模式初始化期間解決,並且帶有[SupplyParameterFromPersistentComponentState]屬性的屬性將會被取消序列化。

註釋 / 注意

支援的僅限於可持續的範圍服務。

CounterService.cs

public class CounterService
{
    [SupplyParameterFromPersistentComponentState]
    public int CurrentCount { get; set; }

    public void IncrementCount()
    {
        CurrentCount++;
    }
}

Program.cs 中:

builder.Services.AddPersistentService<CounterService>(RenderMode.InteractiveAuto);

從實際的服務實例識別串行化屬性:

  • 此方法允許將抽象概念標示為持續性服務。
  • 可讓實際實作成為內部或不同類型的實作。
  • 支援不同組件中的共享程式碼。
  • 導致每個實例顯示相同的屬性。

若要將狀態與 [SupplyParameterFromPersistentComponentState] 屬性一起保存,除了使用宣告式模型外,您還可以直接使用 PersistentComponentState 服務,這樣在處理複雜的狀態持續性情況時會有更高的靈活性。 呼叫 PersistentComponentState.RegisterOnPersisting 以註冊回呼,以在預呈現期間保持元件狀態。 當元件以互動方式呈現時,會擷取狀態。 在初始化程式代碼結束時進行呼叫,以避免在應用程式關機期間發生潛在的競爭狀況。

以下的計數器組件範例會在預渲染期間持續保存計數器狀態,並檢索狀態以初始化元件。

PrerenderedCounter3.razor

@page "/prerendered-counter-3"
@implements IDisposable
@inject ILogger<PrerenderedCounter3> Logger
@inject PersistentComponentState ApplicationState

<PageTitle>Prerendered Counter 3</PageTitle>

<h1>Prerendered Counter 3</h1>

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

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

@code {
    private int currentCount;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override void OnInitialized()
    {
        if (!ApplicationState.TryTakeFromJson<int>(
            nameof(currentCount), out var restoredCount))
        {
            currentCount = Random.Shared.Next(100);
            Logger.LogInformation("currentCount set to {Count}", currentCount);
        }
        else
        {
            currentCount = restoredCount!;
            Logger.LogInformation("currentCount restored to {Count}", currentCount);
        }

        // Call at the end to avoid a potential race condition at app shutdown
        persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
    }

    private Task PersistCount()
    {
        ApplicationState.PersistAsJson(nameof(currentCount), currentCount);

        return Task.CompletedTask;
    }

    private void IncrementCount() => currentCount++;

    void IDisposable.Dispose() => persistingSubscription.Dispose();
}

當元件執行時,currentCount 只在預渲染期間設定一次。 當元件重新渲染時,值將被恢復。 以下是範例輸出。

註釋 / 注意

如果應用程式採用 互動式路由 ,且頁面是透過內部 增強式導覽來連線,則不會發生預先呈現。 因此,您必須針對元件執行完整頁面重載,才能看到下列輸出。 如需詳細資訊,請參閱 互動式路由和預先呈現 一節。

info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount restored to 96

若要保留預先呈現的狀態,請決定要使用 PersistentComponentState 服務保存哪些狀態。 PersistentComponentState.RegisterOnPersisting 註冊一個回呼函數,以在預先呈現期間持久化元件狀態。 當元件以互動方式呈現時,會擷取狀態。 在初始化程式代碼結束時進行呼叫,以避免在應用程式關機期間發生潛在的競爭狀況。

以下的計數器組件範例會在預渲染期間持續保存計數器狀態,並檢索狀態以初始化元件。

PrerenderedCounter2.razor

@page "/prerendered-counter-2"
@implements IDisposable
@inject ILogger<PrerenderedCounter2> Logger
@inject PersistentComponentState ApplicationState

<PageTitle>Prerendered Counter 2</PageTitle>

<h1>Prerendered Counter 2</h1>

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

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

@code {
    private int currentCount;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override void OnInitialized()
    {
        if (!ApplicationState.TryTakeFromJson<int>(
            nameof(currentCount), out var restoredCount))
        {
            currentCount = Random.Shared.Next(100);
            Logger.LogInformation("currentCount set to {Count}", currentCount);
        }
        else
        {
            currentCount = restoredCount!;
            Logger.LogInformation("currentCount restored to {Count}", currentCount);
        }

        // Call at the end to avoid a potential race condition at app shutdown
        persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
    }

    private Task PersistCount()
    {
        ApplicationState.PersistAsJson(nameof(currentCount), currentCount);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose() => persistingSubscription.Dispose();

    private void IncrementCount() => currentCount++;
}

當元件執行時,currentCount 只在預渲染期間設定一次。 當元件重新渲染時,值將被恢復。 以下是範例輸出。

註釋 / 注意

如果應用程式採用 互動式路由 ,且頁面是透過內部 增強式導覽來連線,則不會發生預先呈現。 因此,您必須針對元件執行完整頁面重載,才能看到下列輸出。 如需詳細資訊,請參閱 互動式路由和預先呈現 一節。

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96

通過使用與預渲染時相同的狀態來初始化組件,可以確保任何高成本的初始化步驟只執行一次。 轉譯的 UI 也會符合預先轉譯的 UI,因此瀏覽器中不會發生閃爍。

持久化的預渲染狀態會被傳送到客戶端,用於恢復組件狀態。 在客戶端渲染(CSR, InteractiveWebAssembly)時,資料會暴露給瀏覽器,因此不得包含敏感和私人信息。 在互動式伺服器端轉譯期間(互動式 SSR, InteractiveServer), ASP.NET Core Data Protection 可確保數據安全地傳輸。 InteractiveAuto 渲染模式結合了 WebAssembly 和伺服器的互動性,因此有必要考慮數據暴露給瀏覽器的情況,就像在 CSR 情況一樣。

嵌入頁面和視圖的元件(Razor 頁面/MVC)

對於嵌入於 Pages 或 MVC 應用程式的頁面或檢視中的元件,您必須在應用程式版面佈局結尾的 Razor 標記內,新增 保存元件狀態標籤協助程式,並在其中使用 <persist-component-state /> HTML 標籤。 僅需要對Razor頁面和MVC應用程式進行此操作。 如需詳細資訊,請參閱 ASP.NET Core 中的持久化元件狀態標籤輔助工具

Pages/Shared/_Layout.cshtml

<body>
    ...

    <persist-component-state />
</body>

互動式路由和預渲染

Routes 元件未定義渲染模式時,應用程式會使用逐頁/元件互動和導航。 使用每頁/元件導覽時,應用程式變成互動式之後,內部†導覽將由增強式路由來處理。 在此內容中†Internal 表示導覽事件的URL目的地是 Blazor 應用程式內的端點。

PersistentComponentState 服務只能在初始頁面載入上運作,而不適用於內部增強式頁面流覽事件。

如果應用程式執行完整(非增強型)導航至使用持久性元件狀態的頁面,當應用程式開始具有互動性時,該持久狀態將可供使用。

如果已建立互動式電路,並且在執行增強的導航時使用持久性元件狀態導航到一個頁面,則該狀態在現有電路中不會提供給元件使用。 內部頁面要求沒有預渲染,且 PersistentComponentState 服務不知道已發生增強式導覽。 沒有機制可傳遞狀態更新至已在現有線路上執行的元件。 原因是 Blazor 只支援在運行時間初始化時將狀態從伺服器傳遞至用戶端,而不是在運行時間啟動之後。

正在考慮在 .NET 10(2025 年 11 月)中對 Blazor 架構進行額外工作,以解決此情況。 如需 不支援因應措施的詳細資訊和社群討論,請參閱 在增強式頁面導覽 (dotnet/aspnetcore #51584) 之間支持持續性元件狀態。 ‡不受支援的替代方案未獲得 Microsoft 核准用於 Blazor 應用程式。 以您自己的風險使用第三方套件、方法和程序代碼。

停用增強式導覽會影響效能,但也能避免針對內部頁面要求時載入狀態PersistentComponentState的問題,這部分內容在ASP.NET CoreBlazor 路由和導覽中有所提及。

預渲染指導

預先呈現指引依主題分類於 Blazor 文件中。 以下鏈接涵蓋了整個文檔中按主題分類的所有預渲染指導。

針對 .NET 7 或更早版本,請參閱 Blazor WebAssembly 安全性其他案例:使用驗證預先呈現。 檢視本節中的內容之後,請將檔文章版本選取器下拉式清單重設為最新的 .NET 版本,以確保後續流覽時載入最新版本的文件頁面。