註釋 / 注意
這不是本文的最新版本。 如需目前的版本,請參閱 本文的 .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 文件中。 以下鏈接涵蓋了整個文檔中按主題分類的所有預渲染指導。
基本原理
元件
-
在預先呈現期間控制
<head>
內容 - 渲染模式
-
Razor 與預渲染相關的元件生命週期主題
-
元件初始化 (
OnInitialized{Async}
) -
元件渲染之後(
OnAfterRender{Async}
) - 預先呈現之後的具狀態重新連線
- 使用 JavaScript Interop 預先呈現:本節也會出現在兩 JS 篇 Interop 文章中,說明從 .NET 呼叫 JavaScript,以及從 JavaScript 呼叫 .NET。
- 處理呈現時不完整的非同步操作:由於伺服器上預先呈現期間長時間執行的生命週期工作而導致呈現延遲的指南。
-
元件初始化 (
-
QuickGrid
元件範例應用程式: QuickGrid 範例 Blazor 應用程式的 裝載於 GitHub 頁面。 由於使用社群維護BlazorWasmPrerendering.Build
的 GitHub 專案進行靜態預先呈現,網站會快速載入。 - 將元件整合到 Razor Pages 和 MVC 應用程式時預先呈現
-
在預先呈現期間控制
身份驗證與授權
狀態管理:處理預渲染:除了 處理預渲染 區段之外,文章的其他幾節還包括對預渲染的相關說明。
針對 .NET 7 或更早版本,請參閱 Blazor WebAssembly 安全性其他案例:使用驗證預先呈現。 檢視本節中的內容之後,請將檔文章版本選取器下拉式清單重設為最新的 .NET 版本,以確保後續流覽時載入最新版本的文件頁面。