共用方式為


具有靜態伺服器端轉譯 (靜態 SSR) 的 ASP.NET Core Blazor JavaScript

注意

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

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

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

本文說明如何在具有靜態伺服器端轉譯 (靜態 SSR) 及JS的 Blazor Web App載入 JavaScript ()。

某些應用程式依賴 JS 來執行每個頁面專屬的初始化工作。 使用 Blazor 的增強式導覽功能時,可讓使用者避免重新載入整個頁面,每次發生增強式頁面導覽時,可能不會如預期般再次執行頁面特定的 JS。

為了避免這個問題,我們不建議依賴放在套用至元件的版面配置檔案之外的頁面特定 <script> 元素。 相反地,腳本應該註冊 afterWebStartedJS 初始化表達式 來執行初始化邏輯,並使用事件接聽程式來接聽增強式導覽所造成的頁面更新。

事件

在下列事件接聽程式範例中,{CALLBACK} 佔位元符是回呼函式。

  • 增強式導航開始(enhancednavigationstart)會在增強式導航發生之前觸發回呼:

    blazor.addEventListener("enhancednavigationstart", {CALLBACK});
    
  • 增強式瀏覽結束 (enhancednavigationend) 會在增強式導覽發生之後觸發回呼:

    blazor.addEventListener("enhancednavigationend", {CALLBACK});
    
  • 增強式導覽頁面載入(enhancedload)會在頁面因增強式導覽更新(包括 串流更新)時,每次觸發回呼。

    blazor.addEventListener("enhancedload", {CALLBACK});
    

為了避免這個問題,我們不建議依賴放在套用至元件的版面配置檔案之外的頁面特定 <script> 元素。 相反地,腳本應該註冊 afterWebStartedJS 初始化器 以執行初始化邏輯,並使用事件監聽器來監聽增強式導覽所造成的頁面更新:

blazor.addEventListener("enhancedload", {CALLBACK});

在前面的範例中,{CALLBACK} 佔位符是回呼函數。

增強的頁面載入腳本範例

下列範例示範的方式可將 JS 程式碼設定為在最初載入或更新具有增強式導覽的靜態轉譯頁面時執行。

下列 PageWithScript 元件範例是應用程式中的元件,其需要指令碼才能搭配靜態 SSR 和增強式導覽執行。 下列元件範例包含新增至本文稍後解決方案的 PageScript 類別庫 (RCL) 中的 Razor 元件。

Components/Pages/PageWithScript.razor

@page "/page-with-script"
@using BlazorPageScript

<PageTitle>Enhanced Load Script Example</PageTitle>

<PageScript Src="./Components/Pages/PageWithScript.razor.js" />

Welcome to my page.

在 Blazor Web App,新增下列 共置 JS 檔案

  • 當指令碼新增至頁面時,就會呼叫 onLoad
  • 當指令碼在增強式更新之後仍存在於頁面時,就會呼叫onUpdate
  • 當指令碼在增強式更新之後從頁面移除時,就會呼叫 onDispose

Components/Pages/PageWithScript.razor.js

export function onLoad() {
  console.log('Loaded');
}

export function onUpdate() {
  console.log('Updated');
}

export function onDispose() {
  console.log('Disposed');
}

在名為 BlazorPageScriptRazor 類別庫(RCL) 中,新增下列模組,這是一個 JS 初始化器

wwwroot/BlazorPageScript.lib.module.js

const pageScriptInfoBySrc = new Map();

function registerPageScriptElement(src) {
  if (!src) {
    throw new Error('Must provide a non-empty value for the "src" attribute.');
  }

  let pageScriptInfo = pageScriptInfoBySrc.get(src);

  if (pageScriptInfo) {
    pageScriptInfo.referenceCount++;
  } else {
    pageScriptInfo = { referenceCount: 1, module: null };
    pageScriptInfoBySrc.set(src, pageScriptInfo);
    initializePageScriptModule(src, pageScriptInfo);
  }
}

function unregisterPageScriptElement(src) {
  if (!src) {
    return;
  }

  const pageScriptInfo = pageScriptInfoBySrc.get(src);
  
  if (!pageScriptInfo) {
    return;
  }

  pageScriptInfo.referenceCount--;
}

async function initializePageScriptModule(src, pageScriptInfo) {
  if (src.startsWith("./")) {
    src = new URL(src.substr(2), document.baseURI).toString();
  }

  const module = await import(src);

  if (pageScriptInfo.referenceCount <= 0) {
    return;
  }

  pageScriptInfo.module = module;
  module.onLoad?.();
  module.onUpdate?.();
}

function onEnhancedLoad() {
  for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
    if (referenceCount <= 0) {
      module?.onDispose?.();
      pageScriptInfoBySrc.delete(src);
    }
  }

  for (const { module } of pageScriptInfoBySrc.values()) {
    module?.onUpdate?.();
  }
}

export function afterWebStarted(blazor) {
  customElements.define('page-script', class extends HTMLElement {
    static observedAttributes = ['src'];

    attributeChangedCallback(name, oldValue, newValue) {
      if (name !== 'src') {
        return;
      }

      this.src = newValue;
      unregisterPageScriptElement(oldValue);
      registerPageScriptElement(newValue);
    }

    disconnectedCallback() {
      unregisterPageScriptElement(this.src);
    }
  });

  blazor.addEventListener('enhancedload', onEnhancedLoad);
}

請勿 不要<script> 標籤新增至應用程式的根元件,通常是 App componennt,BlazorPageScript.lib.module.js 因為此案例中的模組是 JS 初始化表達式(afterWebStarted。 JS 初始化器會被 Blazor 框架自動偵測並載入。

在 RCL 中,新增下列 PageScript 元件。

PageScript.razor

<page-script src="@Src"></page-script>

@code {
    [Parameter]
    [EditorRequired]
    public string Src { get; set; } = default!;
}

PageScript 元件通常會在頁面的最上層運作。

如果將 PageScript 元件放在應用程式的版面配置中 (例如 MainLayout.razor),這會導致使用此版面配置的頁面間有共用的 PageScript,則元件只會在完整頁面重新載入之後執行 onLoad,以及在發生任何增強式頁面更新 (包括增強式導覽) 時執行 onUpdate

若要在頁面間重複使用相同的模組,但在每個頁面變更上叫用 onLoadonDispose 回呼,請將查詢字元串附加至指令碼結尾,使其辨識為不同的模組。 應用程式可採用使用元件名稱作為查詢字串值的慣例。 在下列範例中,查詢字串為 "counter",因為這個 PageScript 元件參考放在 Counter 元件中。 這只是建議,您可使用任何您偏好的查詢字串配置。

<PageScript Src="./Components/Pages/PageWithScript.razor.js?counter" />

若要監視特定 DOM 元素中的變更,請使用用戶端上 MutationObserver 中的 JS 模式。 如需詳細資訊,請參閱 ASP.NET Core Blazor JavaScript 互通性 (JS Interop)

不使用 RCL 的實作

本文所述的方法可以直接在 Blazor Web App 中實作,而不需使用 Razor 類別庫 (RCL),方法是將 RCL 的資產移至應用程式。 不過,使用 RCL 很方便,因為 RCL 可以封裝到 NuGet 套件中,以供組織 Blazor 應用程式取用。

如果您將資產移至 Blazor Web App,務必依據 JS 初始化運算式的檔案命名規則,將模組重新命名為BlazorPageScript.lib.module.js,以便符合應用程式。 如果檔案未正確命名,Blazor 無法偵測和載入模組,且應用程式啟動時不會自動執行 afterWebStarted 事件。