ASP.NET Core Blazor JavaScript con representación estática del lado servidor (SSR estática)
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
En este artículo se explica cómo cargar JavaScript (JS) en una Blazor Web App con representación estática del lado servidor (SSR estática) y navegación mejorada.
Algunas aplicaciones dependen de JS para realizar tareas de inicialización específicas de cada página. Cuando se usa la característica de navegación mejorada de Blazor, que permite al usuario evitar volver a cargar toda la página, es posible que el JS específico de la página no se vuelva a ejecutar según lo esperado cada vez que se produzca una navegación de página mejorada.
Para evitar este problema, no se recomienda confiar en elementos específicos <script>
de página colocados fuera del archivo de diseño aplicado al componente. En su lugar, los scripts deben registrar un afterWebStarted
inicializador JS para realizar la lógica de inicialización y utilizar un detector de eventos para escuchar las actualizaciones de la página causadas por la navegación mejorada.
Eventos
En los siguientes ejemplos de agente de escucha de eventos, el marcador de posición {CALLBACK}
es la función de devolución de llamada.
El inicio de navegación mejorada (
enhancednavigationstart
) desencadena una devolución de llamada antes de que se produzca una navegación mejorada:blazor.addEventListener("enhancednavigationstart", {CALLBACK});
El extremo de navegación mejorada (
enhancednavigationend
) desencadena una devolución de llamada después de que se produzca una navegación mejorada:blazor.addEventListener("enhancednavigationend", {CALLBACK});
La carga de páginas de navegación mejorada (
enhancedload
) desencadena una devolución de llamada cada vez que la página se actualiza debido a una navegación mejorada, incluidas actualizaciones de streaming:blazor.addEventListener("enhancedload", {CALLBACK});
Para evitar este problema, no se recomienda confiar en elementos específicos <script>
de página colocados fuera del archivo de diseño aplicado al componente. En su lugar, los scripts deben registrar un inicializador afterWebStarted
JS para realizar la lógica de inicialización y usar un agente de escucha de eventos para escuchar las actualizaciones de páginas causadas por la navegación mejorada:
blazor.addEventListener("enhancedload", {CALLBACK});
En el ejemplo anterior, el marcador de posición {CALLBACK}
es la función de devolución de llamada.
Ejemplo mejorado del script de carga de páginas
En el ejemplo siguiente se muestra una manera de configurar el código JS para que se ejecute cuando se carga o actualiza inicialmente una página representada estáticamente con navegación mejorada.
El siguiente ejemplo de componente PageWithScript
es un componente de la aplicación que requiere que los scripts se ejecuten con SSR estático y navegación mejorada. En el siguiente ejemplo de componente se incluye un componente PageScript
de una biblioteca de clases (RCL) Razor que se agrega a la solución más adelante en este artículo.
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.
En la Blazor Web App, agrega el siguiente archivo JS colocado:
onLoad
se llama cuando se agrega el script a la página.onUpdate
se llama cuando el script todavía existe en la página después de una actualización mejorada.onDispose
se llama cuando se quita el script de la página después de una actualización mejorada.
Components/Pages/PageWithScript.razor.js
:
export function onLoad() {
console.log('Loaded');
}
export function onUpdate() {
console.log('Updated');
}
export function onDispose() {
console.log('Disposed');
}
En una Razor biblioteca de clases (RCL) (el RCL de ejemplo se denomina BlazorPageScript
), agregue el siguiente módulo, que es un inicializador 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);
}
No agregue una etiqueta <script>
al componente raíz de la aplicación, normalmente el componente App
, para BlazorPageScript.lib.module.js
porque en este caso el módulo es un inicializador JS (afterWebStarted
). Los inicializadores JS se detectan y cargan automáticamente mediante el marco Blazor.
En la RCL, agregue el siguiente componente PageScript
.
PageScript.razor
:
<page-script src="@Src"></page-script>
@code {
[Parameter]
[EditorRequired]
public string Src { get; set; } = default!;
}
El componente PageScript
funciona normalmente en el nivel superior de una página.
Si coloca el componente PageScript
en el diseño de una aplicación (por ejemplo, MainLayout.razor
), lo que da como resultado un PageScript
compartido entre las páginas que usan el diseño, el componente solo se ejecuta onLoad
después de que se vuelva a cargar una página completa y onUpdate
cuando se produzca cualquier actualización de página mejorada, incluida la navegación mejorada.
Para volver a usar el mismo módulo entre páginas, pero tiene el onLoad
y onDispose
devoluciones de llamada invocadas en cada cambio de página, anexe una cadena de consulta al final del script para que se reconozca como un módulo diferente. Una aplicación podría adoptar la convención de usar el nombre del componente como valor de cadena de consulta. En el ejemplo siguiente, la cadena de consulta es "counter
" porque esta referencia de componente PageScript
se coloca en un componente de Counter
. Esto es simplemente una sugerencia y puede usar cualquier esquema de cadena de consulta que prefiera.
<PageScript Src="./Components/Pages/PageWithScript.razor.js?counter" />
Para supervisar los cambios en elementos DOM específicos, use el patrón MutationObserver
en JS en el cliente. Para más información, vea Interoperabilidad de JavaScript en Blazor de ASP.NET Core (interoperabilidad de JS).
Implementación sin usar una RCL
El enfoque descrito en este artículo se puede implementar directamente en un Blazor Web App sin usar una biblioteca de clases (RCL) de Razor moviendo los recursos de la RCL a la aplicación. Sin embargo, el uso de una RCL es cómodo porque la RCL se puede empaquetar en un paquete NuGet para su consumo en aplicaciones Blazor de una organización.
Si mueve los recursos a un Blazor Web App, asegúrese de renombrar el módulo (BlazorPageScript.lib.module.js
) para que coincida con la aplicación, siguiendo las reglas de nomenclatura de archivos para los inicializadores JS. Si el archivo no se llama correctamente, Blazor no puede detectar y cargar el módulo y el evento afterWebStarted
no se ejecuta automáticamente cuando se inicia la aplicación.