JavaScript isolado no Blazor com arquivos JS colocados

· 3 min de leitura

Se você já trabalhou com Blazor e JS Interop antes, provavelmente acabou com um enorme arquivo app.js cheio de funções aleatórias para diferentes componentes. Funciona, mas fica confuso rapidamente. Felizmente, existe uma abordagem muito mais limpa: arquivos JavaScript colocados.

A ideia

Assim como o isolamento CSS, o Blazor permite colocar um arquivo .razor.js próximo ao seu componente. O módulo JavaScript é carregado sob demanda, somente quando o componente realmente precisa dele. Sem scripts globais, sem poluição.

#Configurando

Digamos que temos um componente Clipboard.razor que copia texto para a área de transferência. Crie um arquivo chamado Clipboard.razor.js ao lado dele:

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

Observe a palavra-chave export - isso é importante. Blazor carrega-o como um módulo ES padrão.

Carregando o módulo no Blazor

No seu componente, você usa IJSRuntime para importar o módulo. O caminho segue uma convenção: ./_content/{ASSEMBLY_NAME}/{COMPONENT_PATH}.razor.js para bibliotecas, ou apenas o caminho relativo para o projeto atual.

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

Algumas coisas a serem observadas aqui:

  • Carregamos o módulo em OnAfterRenderAsync porque JS Interop não está disponível durante a pré-renderização do lado do servidor
  • Mantemos uma referência ao módulo com IJSObjectReference
  • Implementamos IAsyncDisposable para limpar o módulo quando o componente é destruído

Por que isso é melhor

Antes, eu costumava despejar tudo em um único wwwroot/js/app.js. Funcionou, mas encontrar funções era uma tarefa difícil, e cada página carregava JavaScript desnecessário. Com arquivos JS colocados:

  • Cada componente possui seu próprio JavaScript
  • Os módulos são carregados lentamente, somente quando necessário
  • Sem conflitos de nomes de funções globais
  • Mais fácil de manter e excluir — ao excluir o componente, você exclui o JS com ele

Uma pegadinha com o caminho

O caminho que você passa para import depende se você está em um aplicativo Blazor independente ou em uma biblioteca de classes Razor. Para um aplicativo Blazor normal, o caminho é relativo a wwwroot. O arquivo .razor.js é copiado lá no momento da construção, então você faz referência a ele a partir do local do componente no projeto.

Se você receber um 404 ao carregar o módulo, verifique novamente o caminho e certifique-se de que o arquivo termine em .razor.js, não apenas em .js.

Espero que você tenha gostado da postagem! Sinta-se à vontade para entrar em contato comigo em qualquer mídia social em @emimontesdeoca.

Recursos