Share via


預先轉譯 ASP.NET Core Razor 元件

本文說明 Blazor Web Apps 中伺服器轉譯元件的 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

第一個記錄的計數會在預先轉譯期間發生。 重新轉譯元件後,會再次設定此計數。 當計數從 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 文件中組織。 下列連結涵蓋依主題設定的文件內的所有預先轉譯指引: