Introdução ao WebAssembly e C#: da teoria à prática
\nO WebAssembly (WASM) tornou-se o padrão para aplicações web de alto desempenho. O ecossistema .NET oferece ferramentas poderosas para compilar código C# em WASM, abrindo possibilidades para transferir algoritmos complexos, bibliotecas e até frameworks inteiros para o navegador. No nível avançado, trabalhar com WebAssembly e C# exige compreensão não apenas da compilação básica, mas também dos detalhes do gerenciamento de memória, interação com JavaScript (JS Interop), otimização do tamanho do arquivo binário e depuração.
\nNeste artigo, abordaremos os principais aspectos que permitirão ir além de exemplos simples e criar aplicações WASM eficientes e escaláveis em C#.
\n\n1. Compilação e implantação do .NET no WebAssembly: métodos avançados
\nPara compilar C# em WASM, utiliza-se o ambiente de execução Mono/WASM, incorporado no Blazor WebAssembly ou disponível através do .NET WASM Build Tools. No nível avançado, é importante controlar o processo de build.
\n\n1.1. Otimização do tamanho do build e compilação AOT
\nPor padrão, o Blazor usa compilação JIT dentro do ambiente WASM, o que resulta no carregamento completo do ambiente Mono (~2-3 MB compactado). Para reduzir o tamanho, pode-se usar a compilação AOT (Ahead-of-Time). Ela compila seu código C# diretamente em instruções WASM, eliminando a necessidade de JIT.
\n<!-- No arquivo .csproj -->\n<PropertyGroup>\n <RunAOTCompilation>true</RunAOTCompilation>\n <WasmDebugLevel>0</WasmDebugLevel>\n <WasmNativeStrip>true</WasmNativeStrip>\n</PropertyGroup>\nNo entanto, o AOT aumenta o tempo de build e o tamanho do arquivo final (mas reduz o tamanho do ambiente carregado). Configure WasmDebugLevel e WasmNativeStrip para remover informações de depuração.
1.2. Lazy Loading e divisão de assemblies
\nPara aplicações grandes, use o Lazy Loading. Você pode carregar bibliotecas .NET apenas sob demanda. No Blazor, isso é feito através do LazyAssemblyLoader:
@inject LazyAssemblyLoader Loader\n\n@code {\n private async Task LoadHeavyLibrary()\n {\n var assemblies = await Loader.LoadAssembliesAsync(new[] { "HeavyLibrary.dll" });\n // Agora a biblioteca está disponível\n }\n}\nIsso é crítico para SEO e UX, pois o carregamento inicial permanece rápido.
\n\n2. Interação com JavaScript (JS Interop) em nível profundo
\nTrabalhar com WebAssembly em nível avançado requer uma troca eficiente de dados entre C# e JS. O IJSRuntime padrão opera através de serialização JSON, o que é lento para grandes volumes de dados.
2.1. Trabalho direto com memória via Span e IntPtr
\nUse System.Runtime.InteropServices.JavaScript (disponível no .NET 7+). Você pode obter ponteiros para arrays JS e trabalhar diretamente com eles, evitando cópias.
using System.Runtime.InteropServices.JavaScript;\n\n// Exportar função C# para JS\n[JSExport]\npublic static unsafe int ProcessData(int* dataPtr, int length)\n{\n Span<byte> span = new Span<byte>(dataPtr, length);\n // Processamento de alto desempenho no local\n return span.Length;\n}\n\n// Importar função do JS\n[JSImport("window.processResult", "MyModule")]\npublic static partial void ProcessResult(int result);\nEssa abordagem permite processar imagens, áudio ou grandes arrays de dados (por exemplo, do WebGL) sem custos adicionais de marshaling.
\n\n2.2. JS Interop síncrono
\nPor padrão, as chamadas JS a partir do WASM são assíncronas. Para cenários que exigem sincronia (por exemplo, em loops de jogos), use [JSImport] com IsBlocking = true (disponível no .NET 8+). Isso permite bloquear a thread WASM até receber uma resposta do JS.
[JSImport("getTimestamp", "MyModule", IsBlocking = true)]\npublic