Introduction à WebAssembly et C# : de la théorie à la pratique
\nWebAssembly (WASM) est devenu la norme pour les applications web hautes performances. L'écosystème .NET offre des outils puissants pour compiler du code C# en WASM, ouvrant des possibilités pour transférer des algorithmes complexes, des bibliothèques et même des frameworks entiers dans le navigateur. Au niveau avancé, travailler avec WebAssembly et C# nécessite de comprendre non seulement la compilation de base, mais aussi les subtilités de la gestion de la mémoire, de l'interaction avec JavaScript (JS Interop), de l'optimisation de la taille du fichier binaire et du débogage.
\nDans cet article, nous examinerons les aspects clés qui vous permettront de dépasser les simples exemples et de créer des applications WASM efficaces et évolutives en C#.
\n\n1. Compilation et déploiement de .NET en WebAssembly : méthodes avancées
\nPour compiler C# en WASM, on utilise l'environnement d'exécution Mono/WASM, intégré dans Blazor WebAssembly ou accessible via .NET WASM Build Tools. Au niveau avancé, il est important de contrôler le processus de construction.
\n\n1.1. Optimisation de la taille de la build et compilation AOT
\nPar défaut, Blazor utilise la compilation JIT dans l'environnement WASM, ce qui entraîne le chargement complet de l'environnement Mono (~2-3 Mo compressé). Pour réduire la taille, vous pouvez utiliser la compilation AOT (Ahead-of-Time). Elle compile votre code C# directement en instructions WASM, éliminant ainsi le besoin de JIT.
\n<!-- Dans le fichier .csproj -->\n<PropertyGroup>\n <RunAOTCompilation>true</RunAOTCompilation>\n <WasmDebugLevel>0</WasmDebugLevel>\n <WasmNativeStrip>true</WasmNativeStrip>\n</PropertyGroup>\nCependant, AOT augmente le temps de construction et la taille du fichier final (mais réduit la taille de l'environnement chargé). Configurez WasmDebugLevel et WasmNativeStrip pour supprimer les informations de débogage.
1.2. Lazy Loading et division des assemblies
\nPour les grandes applications, utilisez le Lazy Loading. Vous pouvez charger les bibliothèques .NET uniquement à la demande. Dans Blazor, cela se fait via LazyAssemblyLoader :
@inject LazyAssemblyLoader Loader\n\n@code {\n private async Task LoadHeavyLibrary()\n {\n var assemblies = await Loader.LoadAssembliesAsync(new[] { "HeavyLibrary.dll" });\n // Maintenant la bibliothèque est disponible\n }\n}\nC'est crucial pour le SEO et l'UX, car le chargement initial reste rapide.
\n\n2. Interaction avec JavaScript (JS Interop) à un niveau approfondi
\nLe travail avancé avec WebAssembly nécessite un échange efficace de données entre C# et JS. Le IJSRuntime standard fonctionne via la sérialisation JSON, ce qui est lent pour de gros volumes de données.
2.1. Travail direct avec la mémoire via Span et IntPtr
\nUtilisez System.Runtime.InteropServices.JavaScript (disponible dans .NET 7+). Vous pouvez obtenir des pointeurs vers des tableaux JS et travailler directement avec eux, évitant ainsi les copies.
using System.Runtime.InteropServices.JavaScript;\n\n// Export d'une fonction C# vers JS\n[JSExport]\npublic static unsafe int ProcessData(int* dataPtr, int length)\n{\n Span<byte> span = new Span<byte>(dataPtr, length);\n // Traitement haute performance sur place\n return span.Length;\n}\n\n// Import d'une fonction depuis JS\n[JSImport("window.processResult", "MyModule")]\npublic static partial void ProcessResult(int result);\nCette approche permet de traiter des images, de l'audio ou de grands tableaux de données (par exemple, depuis WebGL) sans frais généraux de marshaling.
\n\n2.2. JS Interop synchrone
\nPar défaut, les appels JS depuis WASM sont asynchrones. Pour les scénarios nécessitant une synchronisation (par exemple, dans les boucles de jeu), utilisez [JSImport] avec IsBlocking = true (disponible dans .NET 8+). Cela permet de bloquer le thread WASM jusqu'à la réception de la réponse de JS.
[JSImport("getTimestamp", "MyModule", IsBlocking = true)]\npublic