JavaScript Blazor ASP.NET Core avec rendu côté serveur statique

Cet article explique comment charger JavaScript (JS) dans une application web Blazor avec le rendu côté serveur statique et la navigation améliorée.

Certaines applications dépendent de JS pour effectuer des tâches d’initialisation spécifiques à chaque page. Lorsque vous utilisez la fonctionnalité de navigation améliorée de Blazor, qui permet à l’utilisateur d’éviter le rechargement de la page entière, il est possible que l’action JS spécifique à la page ne soit pas réexécutée comme prévu chaque fois qu’une navigation de page améliorée se produit.

Pour éviter ce problème, nous ne recommandons pas de s’appuyer sur des éléments <script> spécifiques à la page placés en dehors du fichier de disposition appliqué au composant. Les scripts doivent plutôt inscrire un initialiseur pour effectuer une logique afterWebStartedJSd’initialisation et utiliser un écouteur d’événements (blazor.addEventListener("enhancedload", callback)) pour écouter les mises à jour de page provoquées par une navigation améliorée.

L’exemple suivant montre une façon de configurer le code JS à exécuter lorsqu’une page rendue statiquement avec une navigation améliorée est initialement chargée ou mise à jour.

L'exemple de composant PageWithScript suivant est un composant de l'application qui nécessite l'exécution de scripts avec SSR statique et une navigation améliorée. L’exemple de composant suivant inclut un composant PageScript d’une bibliothèque de classes Razor (RCL) ajouté à la solution plus loin dans cet article.

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.

Dans l’application web Blazor, ajoutez le fichier JS colocalisé suivant :

  • onLoad est appelé lorsque le script est ajouté à la page.
  • onUpdate est appelé lorsque le script existe toujours sur la page après une mise à jour améliorée.
  • onDispose est appelé lorsque le script est supprimé de la page après une mise à jour améliorée.

Components/Pages/PageWithScript.razor.js:

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

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

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

Dans une bibliothèque de classes Razor (RCL) (l’exemple RCL est nommé BlazorPageScript), ajoutez le module suivant.

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

Ajoutez le composant PageScript suivant à la RCL.

PageScript.razor:

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

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

Le composant PageScript fonctionne normalement au niveau supérieur d’une page.

Si vous placez le composant PageScript dans la mise en page d'une application (par exemple, MainLayout.razor), ce qui entraîne un partage PageScript entre les pages qui utilisent la mise en page, le composant n'exécute onLoad qu'après un rechargement complet de la page et onUpdate lorsqu'une mise à jour de page améliorée se produit, y compris une navigation améliorée.

Pour réutiliser le même module d’une page à l’autre, mais en invoquant les rappels onLoad et onDispose à chaque changement de page, ajoutez une chaîne de requête à la fin du script afin qu’il soit reconnu comme un module différent. Une application peut adopter la convention d’utilisation du nom du composant comme valeur de chaîne de requête. Dans l’exemple suivant, la chaîne de requête est «counter», car cette PageScript référence de composant est placée dans un composant Counter. Il s’agit simplement d’une suggestion, et vous pouvez utiliser le schéma de chaîne de requête que vous préférez.

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

Pour surveiller les modifications dans des éléments DOM spécifiques, utilisez le modèle MutationObserver dans JS sur le client. Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

Exemple d'implémentation sans utiliser de RCL

L'approche décrite dans cet article peut être implémentée directement dans une Web App Blazor sans utiliser de bibliothèque de classes Razor (RCL). Pour obtenir un exemple, consultez Activer la génération de code QR pour les applications d’authentification TOTP dans une application Web Blazor ASP.NET Core.