Condividi tramite


ASP.NET Core Blazor JavaScript con rendering statico lato server (SSR statico)

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo illustra come caricare JavaScript (JS) in un oggetto Blazor Web App con rendering statico lato server (SSR statico) e spostamento avanzato.

Alcune app dipendono JS da per eseguire attività di inizializzazione specifiche di ogni pagina. Quando si usa Blazorla funzionalità di spostamento avanzata, che consente all'utente di evitare di ricaricare l'intera pagina, è possibile che non JS venga eseguito di nuovo come previsto ogni volta che si verifica un spostamento di pagina avanzato.

Per evitare questo problema, non è consigliabile basarsi su elementi specifici <script> della pagina posizionati all'esterno del file di layout applicato al componente. Gli script devono invece registrare un afterWebStartedJS inizializzatore per eseguire la logica di inizializzazione e usare un listener di eventi per rilevare gli aggiornamenti delle pagine causati dalla navigazione potenziata.

Avvenimenti

Negli esempi di Event Listener seguenti, il segnaposto {CALLBACK} è la funzione di callback.

  • L'avvio di navigazione avanzata (enhancednavigationstart) attiva un callback prima che si verifichi una navigazione avanzata.

    blazor.addEventListener("enhancednavigationstart", {CALLBACK});
    
  • L'endpoint di navigazione avanzata (enhancednavigationend) attiva un callback dopo una navigazione avanzata:

    blazor.addEventListener("enhancednavigationend", {CALLBACK});
    
  • Il caricamento avanzato della pagina di navigazione (enhancedload) attiva un callback ogni volta che la pagina viene aggiornata a causa di una navigazione migliorata, inclusi aggiornamenti di streaming:

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

Per evitare questo problema, non è consigliabile basarsi su elementi specifici <script> della pagina posizionati all'esterno del file di layout applicato al componente. Gli script devono invece registrare un inizializzatore afterWebStartedJS per eseguire la logica di inizializzazione e usare un ascoltatore di eventi per captare gli aggiornamenti delle pagine causati dalla navigazione migliorata.

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

Nell'esempio precedente il segnaposto {CALLBACK} è la funzione di callback.

Esempio di script di caricamento pagina avanzato

Nell'esempio seguente viene illustrato un modo per configurare JS il codice da eseguire quando viene inizialmente caricato o aggiornato una pagina sottoposta a rendering statico con navigazione avanzata.

L'esempio di componente seguente PageWithScript è un componente nell'app che richiede l'esecuzione di script con SSR statico e navigazione avanzata. L'esempio di componente seguente include un PageScript componente di una libreria di Razor classi (RCL) aggiunta alla soluzione più avanti in questo articolo.

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 AppIn aggiungere il file collocato JS seguente:

  • onLoad viene chiamato quando lo script viene aggiunto alla pagina.
  • onUpdate viene chiamato quando lo script esiste ancora nella pagina dopo un aggiornamento avanzato.
  • onDispose viene chiamato quando lo script viene rimosso dalla pagina dopo un aggiornamento avanzato.

Components/Pages/PageWithScript.razor.js:

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

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

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

In una libreria di classi Razor (RCL) (l'esempio RCL è denominato BlazorPageScript), aggiungere il seguente modulo, che è un inizializzatore 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);
}

Non aggiungere un tag <script> al componente radice dell'app, in genere il App componente, per BlazorPageScript.lib.module.js perché il modulo in questo caso è un inizializzatore JS (afterWebStarted). JS gli inizializzatori vengono rilevati e caricati automaticamente dal framework Blazor.

Nell'RCL aggiungere il componente seguente PageScript .

PageScript.razor:

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

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

Il PageScript componente funziona normalmente nel livello superiore di una pagina.

Se si inserisce il PageScript componente nel layout di un'app (ad esempio , MainLayout.razor), che genera una condivisione PageScript tra le pagine che usano il layout, il componente viene eseguito onLoad solo dopo un ricaricamento completo della pagina e onUpdate quando si verifica un aggiornamento di pagina avanzato, inclusa la navigazione avanzata.

Per riutilizzare lo stesso modulo tra le pagine, ma richiamare i onLoad callback e onDispose in ogni modifica della pagina, aggiungere una stringa di query alla fine dello script in modo che venga riconosciuta come un modulo diverso. Un'app potrebbe adottare la convenzione di usare il nome del componente come valore della stringa di query. Nell'esempio seguente la stringa di query è "counter" perché questo PageScript riferimento al componente viene inserito in un Counter componente. Si tratta semplicemente di un suggerimento ed è possibile usare qualsiasi schema di stringa di query preferito.

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

Per monitorare le modifiche in elementi DOM specifici, usare il MutationObserver modello in JS nel client. Per altre informazioni, vedere ASP.NET Core JavaScript interoperabilità (interoperabilità).For more information, see ASP.NET Core Blazor JavaScript interoperability (JS interop).

Implementazione senza usare un RCL

L'approccio descritto in questo articolo può essere implementato direttamente in un Blazor Web App senza usare una libreria di classi Razor (RCL) spostando gli asset dell'RCL nell'app. Tuttavia, l'uso di un RCL è utile perché l'RCL può essere inserito in un pacchetto NuGet per l'utilizzo da parte di Blazor app in un'organizzazione.

Se sposti gli asset in un Blazor Web App, assicurati di rinominare il modulo (BlazorPageScript.lib.module.js) in modo che corrisponda all'applicazione secondo le regole di nomenclatura dei file per gli inizializzatori JS. Se il file non è denominato correttamente, Blazor non è in grado di rilevare e caricare il modulo e l'evento afterWebStarted non viene eseguito automaticamente all'avvio dell'app.