預先轉譯 ASP.NET Core Razor 元件

本文說明 Blazor Web Apps 中伺服器轉譯元件的 Razor 元件預先轉譯案例。

預先轉譯是伺服器上初始轉譯頁面內容,而不需要為轉譯的控制項啟用事件處理常式的程序。 伺服器會盡快輸出頁面的 HTML UI,以回應初始要求,如此可改善應用程式對使用者的回應速度。 預先轉譯也可改善搜尋引擎最佳化 (SEO),方法是轉譯搜尋引擎用來計算頁面排名的初始 HTTP 回應的內容。

保存預先轉譯的狀態

若未保存預先轉譯的狀態,在預先轉譯期間所使用的狀態會遺失,而且必須在應用程式完全載入時重建。 如果以非同步方式建立任何狀態,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

第一個記錄的計數會在預先轉譯期間發生。 重新轉譯元件後,會再次設定此計數。 當計數從 41 更新為 92 時,UI 中也會閃爍。

若要在預先轉譯期間保留計數器的初始值,Blazor 會使用 PersistentComponentState 服務支援在預先轉譯的頁面中保存狀態 (以及針對內嵌至 Razor Pages 或 MVC 應用程式的頁面或檢視中的元件,使用 保存元件狀態標記協助程式)。

若要保留預先轉譯的狀態,請決定要使用 PersistentComponentState 服務保存哪個狀態。 PersistentComponentState.RegisterOnPersisting 會註冊回呼,以在應用程式暫停前保存元件狀態。 應用程式繼續時會擷取此狀態。

下列範例示範一般模式:

  • {TYPE} 預留位置代表要保存的資料類型。
  • {TOKEN} 預留位置是狀態識別碼字串。 請考慮使用 nameof({VARIABLE}),其中 {VARIABLE} 預留位置是保存狀態的變數名稱。 針對狀態識別碼使用 nameof() 可避免使用引號字串。
@implements IDisposable
@inject PersistentComponentState ApplicationState

...

@code {
    private {TYPE} data;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistData);

        if (!ApplicationState.TryTakeFromJson<{TYPE}>(
            "{TOKEN}", out var restored))
        {
            data = await ...;
        }
        else
        {
            data = restored!;
        }
    }

    private Task PersistData()
    {
        ApplicationState.PersistAsJson("{TOKEN}", data);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

下列計數器元件範例會在預先轉譯期間保存計數器狀態,並擷取要初始化元件的狀態。

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()
    {
        persistingSubscription =
            ApplicationState.RegisterOnPersisting(PersistCount);

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

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

        return Task.CompletedTask;
    }

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

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

當元件執行時,currentCount 只會在預先轉譯期間設定一次。 重新轉譯元件時,會還原此值。 以下是範例輸出。

注意

如果應用程式採用 互動式(增強式)路由 ,且透過內部導覽到達頁面,則不會進行預先轉譯。 因此,您必須對 PrerenderedCounter2 元件執行完整頁面重新載入,才能看到下列輸出。

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

在預先轉譯期間使用相同的狀態初始化元件,任何昂貴的初始化步驟只會執行一次。 轉譯的 UI 也會符合預先轉譯的 UI,因此瀏覽器中不會發生閃爍。

嵌入至頁面和檢視的元件(Razor Pages/MVC)

對於內嵌至 Razor Pages 和 MVC 應用程式的頁面或檢視中的元件,您必須在應用程式版面配置的結尾 </body> 標記內新增具有 <persist-component-state /> HTML 標記的保存元件狀態標記協助程式只有 Razor Pages 和 MVC 應用程式才需要此協助程式。 如需詳細資訊,請參閱 ASP.NET Core 中的保存元件狀態標記協助程式

Pages/Shared/_Layout.cshtml

<body>
    ...

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

互動式路由和預先轉譯

互動式路由 的內部瀏覽並不涉及向伺服器要求新的頁面內容。 因此,內部頁面要求不會發生預先轉譯。

PersistentComponentState 服務只能在初始頁面載入運作,而不會在 增強的頁面瀏覽事件之間運作。 如果應用程式執行完整(非增強的)瀏覽至使用永續性元件狀態的頁面,則當應用程式變成互動式時,會提供持續性狀態。 但是,如果已建立互動式線路,且對轉譯持續性元件狀態的頁面執行增強式瀏覽,則該狀態在現有的線路中不可使用。 PersistentComponentState 服務不知道增強的瀏覽,並且沒有機制可傳遞狀態更新至已執行的元件。

預先轉譯指引

預先轉譯指引會依主題在 Blazor 文件中組織。 下列連結涵蓋依主題設定的文件內的所有預先轉譯指引: