Megosztás a következőn keresztül:


ASP.NET Core Blazor JavaScript statikus kiszolgálóoldali rendereléssel (statikus SSR)

Jegyzet

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Ez a cikk bemutatja, hogyan tölthető be a JavaScript (JS) statikus kiszolgálóoldali rendereléssel (statikus SSR) egy Blazor Web App-ben, valamint továbbfejlesztett navigáció.

Egyes alkalmazások az egyes oldalakra jellemző inicializálási feladatok végrehajtásához JS-ra támaszkodnak. Ha a Blazortovábbfejlesztett navigációs funkcióját használja, amely lehetővé teszi a felhasználó számára, hogy elkerülje a teljes oldal újbóli betöltését, előfordulhat, hogy a lapspecifikus JS nem hajtódik végre ismételten az elvárt módon, amikor egy továbbfejlesztett oldalnavigáció történik.

A probléma elkerülése érdekében nem javasoljuk, hogy az összetevőre alkalmazott elrendezésfájlon kívül elhelyezett oldalspecifikus <script> elemekre támaszkodjon. Ehelyett a szkripteknek regisztrálniuk kell egy afterWebStartedJS inicializálót az inicializálási logika végrehajtására, és eseményfigyelőt kell használniuk az oldalfrissítések figyelésére, amelyeket a fejlett navigáció okoz.

Események

Az alábbi eseményfigyelő példákban a {CALLBACK} helyőrző a visszahívási függvény.

  • A továbbfejlesztett navigáció indítása (enhancednavigationstart) visszahívást indít el a továbbfejlesztett navigáció előtt:

    blazor.addEventListener("enhancednavigationstart", {CALLBACK});
    
  • A továbbfejlesztett navigációs végpont (enhancednavigationend) visszahívást indít el a továbbfejlesztett navigáció után:

    blazor.addEventListener("enhancednavigationend", {CALLBACK});
    
  • A továbbfejlesztett navigációs lapbetöltés (enhancedload) visszahívást vált ki minden alkalommal, amikor a lap frissül a továbbfejlesztett navigáció miatt, beleértve streamelési frissítéseket:

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

A probléma elkerülése érdekében nem javasoljuk, hogy az összetevőre alkalmazott elrendezésfájlon kívül elhelyezett oldalspecifikus <script> elemekre támaszkodjon. Ehelyett a szkripteknek regisztrálniuk kell egy afterWebStartedJS inicializálót, amely végrehajtja az inicializálási logikát, és eseményfigyelőt kell használniuk az oldalak frissítéseinek figyeléséhez, amelyeket a továbbfejlesztett navigáció okoz.

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

Az előző példában a {CALLBACK} helyőrző a visszahívási függvény.

Továbbfejlesztett lapbetöltési szkript példa

Az alábbi példa bemutatja, hogyan konfigurálható JS kód futtatása, ha egy statikusan renderelt, továbbfejlesztett navigációval rendelkező lap kezdetben betöltődik vagy frissül.

Az alábbi PageWithScript összetevő-példa egy olyan összetevő az alkalmazásban, amely megköveteli, hogy a szkriptek statikus SSR-vel és továbbfejlesztett navigációval fussanak. Az alábbi összetevő-példa egy PageScript összetevőt tartalmaz egy Razor osztálytárból (RCL), amelyet a cikk későbbi részében a megoldáshoz adunk hozzá.

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.

A(z) Blazor Web App-ben/-ban adja hozzá a következő elhelyezett JS fájl:

  • onLoad akkor van meghívva, amikor a szkriptet hozzáadják az oldalhoz.
  • onUpdate akkor hívjuk meg, ha a szkript egy továbbfejlesztett frissítés után is létezik a lapon.
  • onDispose akkor hívjuk meg, ha a szkript egy továbbfejlesztett frissítés után törlődik a lapról.

Components/Pages/PageWithScript.razor.js:

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

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

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

Egy Razor osztálykódtárban (RCL) (a példa RCL neve BlazorPageScript), adja hozzá a következő modult, amely egy JS inicializáló.

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

Ne adjon hozzá <script> címkét az alkalmazás gyökérkomponenséhez, amely általában a AppBlazorPageScript.lib.module.js komponens, mert a modul ebben az esetben JS inicializáló (afterWebStarted). JS inicializálókat a Blazor keretrendszer automatikusan észleli és betölti.

Az RCL-ben adja hozzá a következő PageScript összetevőt.

PageScript.razor:

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

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

A PageScript összetevő általában a lap felső szintjén működik.

Ha a PageScript összetevőt egy alkalmazás elrendezésében helyezi el (például MainLayout.razor), amely megosztott PageScript eredményez az elrendezést használó lapok között, akkor az összetevő csak a teljes oldal újrabetöltése után fut onLoad, és onUpdate, ha továbbfejlesztett oldalfrissítés történik, beleértve a továbbfejlesztett navigációt is.

Ha ugyanazt a modult szeretné újra felhasználni a lapok között, de az egyes lapmódosítások onLoad és onDispose visszahívásokat szeretné meghívni, fűzjön hozzá egy lekérdezési sztringet a szkript végéhez, hogy az egy másik modulként legyen felismerve. Egy alkalmazás elfogadhatja az összetevő nevének lekérdezési sztringértékként való használatára vonatkozó konvenciót. A következő példában a lekérdezési sztring "counter", mert ez a PageScript összetevőhivatkozás egy Counter összetevőbe kerül. Ez csupán javaslat, és tetszőleges lekérdezési sztringsémát használhat.

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

A konkrét DOM-elemek változásainak figyelésére használja a kliens MutationObserver mintáját a JS-ben. További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).

Implementáció RCL használata nélkül

A cikkben ismertetett megközelítés közvetlenül egy Blazor Web App-ban alkalmazható a Razor osztálykönyvtár (RCL) használata nélkül az RCL eszközeinek az alkalmazásba való áthelyezésével. Az RCL használata azonban kényelmes, mert az RCL egy NuGet-csomagba csomagolható, amelyet Blazor alkalmazások használhatnak egy szervezeten belül.

Ha az objektumokat egy Blazor Web Apphelyezi át, mindenképpen nevezze át a modult (BlazorPageScript.lib.module.js) az alkalmazáshoz igazodva, a JS inicializálókfájlelnevezési szabályai szerint. Ha a fájl neve nem megfelelő, Blazor nem tudja észlelni és betölteni a modult, és a afterWebStarted esemény nem fut automatikusan az alkalmazás indításakor.