Partilhar via


ASP.NET Core Blazor JavaScript com renderização estática do lado do servidor (SSR estático)

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

Este artigo explica como carregar JavaScript (JS) num Blazor Web App com renderização estática do lado do servidor (SSR estático) e navegação melhorada.

Alguns aplicativos dependem de JS para executar tarefas de inicialização específicas de cada página. Ao usar o recurso de navegação aprimorada do Blazor, que permite ao usuário evitar recarregar a página inteira, JS específicos da página podem não ser executados novamente como esperado cada vez que ocorre uma navegação de página aprimorada.

Para evitar esse problema, não recomendamos confiar em elementos de <script> específicos da página colocados fora do arquivo de layout aplicado ao componente. Em vez disso, os scripts devem registrar um afterWebStartedJS inicializador para executar a lógica de inicialização e usar um ouvinte de eventos para ouvir atualizações de página causadas por navegação aprimorada.

Eventos

Nos seguintes exemplos de ouvinte de eventos, o espaço reservado {CALLBACK} é a função de retorno de chamada.

  • O início de navegação avançada (enhancednavigationstart) dispara um retorno de chamada antes de ocorrer uma navegação avançada:

    blazor.addEventListener("enhancednavigationstart", {CALLBACK});
    
  • O término da navegação melhorada (enhancednavigationend) aciona uma chamada de retorno após a ocorrência de uma navegação melhorada.

    blazor.addEventListener("enhancednavigationend", {CALLBACK});
    
  • O carregamento avançado da página de navegação (enhancedload) dispara um retorno de chamada cada vez que a página é atualizada devido a uma navegação aprimorada, incluindo atualizações de streaming :

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

Para evitar esse problema, não recomendamos confiar em elementos de <script> específicos da página colocados fora do arquivo de layout aplicado ao componente. Em vez disso, os scripts devem registar um inicializador para executar a lógica de inicialização e usar um ouvinte de eventos para monitorizar atualizações de página causadas pela navegação melhorada.

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

No exemplo anterior, o espaço reservado {CALLBACK} é a função de retorno de chamada.

Exemplo de script de carregamento de página aprimorado

O exemplo a seguir demonstra uma maneira de configurar JS código para ser executado quando uma página renderizada estaticamente com navegação aprimorada é inicialmente carregada ou atualizada.

O exemplo de componente PageWithScript a seguir é um componente no aplicativo que requer scripts para executar com SSR estático e navegação aprimorada. O exemplo de componente a seguir inclui um componente PageScript de uma biblioteca de classes Razor (RCL) que é adicionado à solução posteriormente neste artigo.

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.

No , adicione o ficheiro colocado no :

  • onLoad é chamado quando o script é adicionado à página.
  • onUpdate é chamado quando o script ainda existe na página após uma atualização aprimorada.
  • onDispose é chamado quando o script é removido da página após uma atualização aprimorada.

Components/Pages/PageWithScript.razor.js:

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

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

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

Em uma Razor Biblioteca de Classes (RCL) (o exemplo RCL é chamado BlazorPageScript), adicione o seguinte módulo, que é um JS inicializador.

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

Não adicionar uma tag <script> ao componente raiz do aplicativo, normalmente o App componennt, por BlazorPageScript.lib.module.js porque o módulo, neste caso, é um inicializador JS (afterWebStarted). JS inicializadores são detetados e carregados automaticamente pelo framework Blazor.

Na RCL, adicione o seguinte componente PageScript.

PageScript.razor:

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

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

O componente PageScript funciona normalmente no nível superior de uma página.

Se você colocar o componente PageScript no layout de um aplicativo (por exemplo, MainLayout.razor), o que resulta em um PageScript compartilhado entre páginas que usam o layout, o componente só será executado onLoad após uma recarga de página inteira e onUpdate quando ocorrer qualquer atualização de página aprimorada, incluindo navegação aprimorada.

Para reutilizar o mesmo módulo entre páginas, mas ter os retornos de chamada onLoad e onDispose invocados em cada alteração de página, acrescente uma cadeia de caracteres de consulta ao final do script para que ele seja reconhecido como um módulo diferente. Um aplicativo pode adotar a convenção de usar o nome do componente como o valor da cadeia de caracteres de consulta. No exemplo a seguir, a cadeia de caracteres de consulta é "counter" porque essa referência de componente PageScript é colocada em um componente Counter. Esta é apenas uma sugestão, e você pode usar qualquer esquema de cadeia de caracteres de consulta que preferir.

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

Para monitorar alterações em elementos DOM específicos, use o padrão MutationObserver em JS no cliente. Para obter mais informações, consulte ASP.NET Core Blazor JavaScript (JS interoperabilidade).

Implementação sem utilização de RCL

A abordagem descrita neste artigo pode ser implementada diretamente em um Blazor Web App sem usar uma biblioteca de classes Razor (RCL) movendo os ativos da RCL para o aplicativo. No entanto, o uso de uma RCL é conveniente porque a RCL pode ser embalada num pacote NuGet para consumo por aplicações Blazor de toda a organização.

Se você mover os ativos para um Blazor Web App, certifique-se de renomear o módulo (BlazorPageScript.lib.module.js) para corresponder ao aplicativo de acordo com as regras de nomenclatura de arquivo para inicializadores JS. Se o arquivo não estiver nomeado corretamente, Blazor não poderá detetar e carregar o módulo e o evento afterWebStarted não será executado automaticamente quando o aplicativo for iniciado.