Un módulo JavaScript es un archivo que exporta una API explícita sin añadir sus funciones al ámbito global.
Blazor aprovecha esta característica nativa de los navegadores para ofrecernos el Aislamiento de JS.
Ventajas:
- Limpieza: No contaminas el
window. - Rendimiento: El script no se descarga al arrancar la app. Se descarga (Lazy Load) solo cuando el usuario entra en la página que usa ese componente.
- Encapsulación: Las funciones y variables internas quedan dentro del módulo (aunque una misma instancia de módulo puede ser compartida por varias importaciones).
Crear el módulo JavaScript
Vamos a crear un script para un editor. En lugar de añadirlo a window, usamos la palabra clave export.
Crea un archivo en wwwroot/js/editor.js:
// wwwroot/js/editor.js
export function iniciarEditor(elemento, mensajeInicial) {
if (elemento) {
elemento.value = mensajeInicial;
elemento.style.border = "2px solid #6610f2"; // Simulamos algo visual
console.log("Editor iniciado");
}
}
export function obtenerTexto(elemento) {
return elemento ? elemento.value : "";
}
export function mostrarAlerta() {
alert("¡Hola desde un módulo aislado!");
}Nota que no hay ningún window.algo = .... Todo lo que quieras usar desde C# debe llevar delante export.
Importar el módulo con IJSObjectReference
Aquí cambia la mecánica respecto a los artículos anteriores.
En lugar de llamar a JS.InvokeVoidAsync directamente sobre una función global, primero debemos importar el archivo.
Al importar un archivo JS dinámicamente, Blazor nos devuelve un objeto de tipo IJSObjectReference. Este objeto es nuestro “mando a distancia” para interactuar con ese archivo específico.
@implements IAsyncDisposable
@inject IJSRuntime JS
<textarea @ref="editor" rows="5"></textarea>
<button @onclick="LeerTexto">Leer Texto</button>
@code {
// Esta variable guardará la referencia al módulo cargado
private IJSObjectReference? moduloJS;
private ElementReference editor;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// 1. Cargamos el archivo dinámicamente
// La ruta es relativa a wwwroot
moduloJS = await JS.InvokeAsync<IJSObjectReference>(
"import", "./js/editor.js");
// 2. Invocamos la función exportada DEL MÓDULO
await moduloJS.InvokeVoidAsync("iniciarEditor", editor, "Escribe aquí...");
}
}
private async Task LeerTexto()
{
if (moduloJS is not null)
{
var texto = await moduloJS.InvokeAsync<string>("obtenerTexto", editor);
Console.WriteLine($"Texto recuperado: {texto}");
}
}
// ... Dispose (ver abajo)
}Fíjate en la diferencia clave:
- Antes:
JS.InvokeVoidAsync("nombreGlobal") - Ahora:
moduloJS.InvokeVoidAsync("nombreExportado")
Liberar la referencia con DisposeAsync
IJSObjectReference representa una referencia de interop al módulo. Debemos liberarla cuando el componente se retire para que el runtime pueda eliminarla de su tabla de referencias.
Para ello, el componente debe implementar IAsyncDisposable (no IDisposable normal, porque la limpieza de JS es asíncrona).
public async ValueTask DisposeAsync()
{
if (moduloJS is not null)
{
try
{
await moduloJS.DisposeAsync();
}
catch (JSDisconnectedException)
{
// Ignorar: esto pasa si el usuario cierra la pestaña del navegador
// y SignalR se desconecta antes de que podamos limpiar.
}
}
}Archivos JavaScript colocados junto al componente
En .NET 6+, Microsoft mejoró esto aún más.
Al igual que podemos tener Counter.razor.css, podemos tener Counter.razor.js.
Si colocas el archivo JS justo al lado del componente (en la carpeta Components o Pages, no en wwwroot), Blazor lo empaquetará automáticamente en una ruta especial.
Estructura de archivos:
Pages/MiComponente.razorPages/MiComponente.razor.js
Cómo cargarlo: En el proyecto de la aplicación, el archivo se importa mediante su ruta pública dentro del propio proyecto:
moduloJS = await JS.InvokeAsync<IJSObjectReference>(
"import", "./Components/Pages/MiComponente.razor.js");Si el componente pertenece a una Razor Class Library, la ruta usa ./_content/{PACKAGE_ID}/... antes de la ruta del archivo.
Ojo con la caché: Durante el desarrollo, a veces los navegadores cachean agresivamente los módulos JS. Si cambias el código JS y no ves cambios, prueba a hacer un “Hard Refresh” (Ctrl + F5) o abre las DevTools y marca “Disable Cache”.