JavaScript aislado en Blazor con archivos JS colocados

· 3 min de lectura

Si ha trabajado con Blazor y JS Interop antes, probablemente haya terminado con un archivo app.js masivo lleno de funciones aleatorias para diferentes componentes. Funciona, pero se complica rápidamente. Afortunadamente, existe un enfoque mucho más claro: archivos JavaScript colocados.

La idea

Al igual que el aislamiento de CSS, Blazor le permite colocar un archivo .razor.js junto a su componente. El módulo JavaScript se carga según demanda, sólo cuando el componente realmente lo necesita. Sin guiones globales, sin contaminación.

Configurarlo

Digamos que tenemos un componente Clipboard.razor que copia texto al portapapeles. Crea un archivo llamado Clipboard.razor.js justo al lado:

export function copyToClipboard(text) {
    navigator.clipboard.writeText(text).then(() => {
        console.log("Copied to clipboard!");
    });
}

Observe la palabra clave export: esto es importante. Blazor lo carga como un módulo ES estándar.

Cargando el módulo en Blazor

En su componente, utiliza IJSRuntime para importar el módulo. La ruta sigue una convención: ./_content/{ASSEMBLY_NAME}/{COMPONENT_PATH}.razor.js para bibliotecas, o simplemente la ruta relativa para el proyecto actual.

@inject IJSRuntime JS
@implements IAsyncDisposable

<button @onclick="Copy">Copy to clipboard</button>

@code {
    [Parameter]
    public string Text { get; set; }

    private IJSObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>(
                "import", "./Components/Clipboard.razor.js");
        }
    }

    private async Task Copy()
    {
        if (module is not null)
        {
            await module.InvokeVoidAsync("copyToClipboard", Text);
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

Algunas cosas a tener en cuenta aquí:

  • Cargamos el módulo en OnAfterRenderAsync porque JS Interop no está disponible durante la renderización previa del lado del servidor.
  • Mantenemos una referencia al módulo con IJSObjectReference
  • Implementamos IAsyncDisposable para limpiar el módulo cuando se destruye el componente.

Por qué esto es mejor

Antes, solía volcar todo en un solo wwwroot/js/app.js. Funcionó, pero encontrar funciones fue complicado y cada página cargaba JavaScript que no necesitaba. Con archivos JS colocados:

  • Cada componente posee su propio JavaScript
  • Los módulos se cargan de forma diferida, sólo cuando es necesario
  • No hay conflictos de nombres de funciones globales
  • Más fácil de mantener y eliminar: cuando elimina el componente, elimina el JS con él.

Un problema con el camino

La ruta que pasa a import depende de si está en una aplicación Blazor independiente o en una biblioteca de clases de Razor. Para una aplicación Blazor normal, la ruta es relativa a wwwroot. El archivo .razor.js se copia allí en el momento de la compilación, por lo que puede hacer referencia a él desde la ubicación del componente en el proyecto.

Si obtiene un error 404 al cargar el módulo, verifique la ruta y asegúrese de que el archivo termine en .razor.js, no solo en .js.

Espero que os haya gustado el post! No dudes en contactarme en cualquier red social en @emimontesdeoca.

Recursos