Master Blazor WebAssembly on .Net6
¿Sabías que es posible crear proyectos Web Assembly “vainilla” en .Net6? Sí, es posible. Aunque no está soportado oficialmente, el nuevo SDK de Blazor WebAssembly es lo suficientemente flexible como para permitirte construir proyectos wasm completos.
Esto es extremadamente útil en muchos escenarios en los que solo se quiere construir una aplicación de cliente web, pero aprovechando toda la potencia de WebAssembly para algoritmos de alta demanda utilizando .Net. Aquí, en el equipo de research de Plain Concepts, hemos desarrollado un conjunto de muestras que ilustran múltiples funcionalidades «ocultas» que puedes hacer con WebAssembly en .Net6, con el fin de cubrir los casos de uso más comunes para aplicaciones de alto rendimiento. ¡Incluso podemos utilizar el depurador de Visual Studio 2022!
Además, hemos añadido algunas funcionalidades que faltaban para interactuar fácilmente con javascript, como tener la posibilidad de utilizar el Sistema de Archivos Virtual (llamando a System.File en .Net), y la compresión Brotli/GZip para entornos de producción. Sigue leyendo para dominar Web Assembly en .Net6.
¿Qué es Blazor WebAssembly?
Blazor es un framework desarrollado por Microsoft pensado para llevar C# al navegador, además de permitir crear aplicaciones SPA. Es similar a otros frameworks Javascript como Angular o React, pero con código .Net. Para funcionar, utiliza WebAssembly con el fin de compilar el código .Net y generar páginas fluidas que no requieran recargar todo el contenido. Como en cualquier otra aplicación web de cliente, esto se traduce en que no se necesita ningún servidor externo para funcionar, por lo que se acaba ahorrando dinero.
Es decir, WebAssembly es una tecnología que permite ejecutar código binario en el navegador, aportando un rendimiento superior a Javascript.
¿Dónde se ejecuta Blazor WebAssembly?
Como explica la propia Microsoft , con Blazor WebAssembly la aplicación cliente y sus dependencias se compilan en código binario y se descargan en paralelo en el navegador web. Cuando un usuario accede a la aplicación, esta se ejecuta de forma muy rápida en el navegador que está usando el usuario final, pues gracias a WebAssembly el código está muy optimizado.
Autenticación en Blazor WebAssembly
Microsoft describe en su web cómo proteger una aplicación independiente Blazor WebAssembly mediante la biblioteca de autenticación Blazor WebAssembly.
Así, cuando se diseña una aplicación para cuentas de usuario individuales, esta recibe al instante una referencia del paquete Microsoft.AspNetCore.Components.WebAssembly.Authentication. Con ello se pueden verificar usuarios y conseguir tokens.
¿Qué son los componentes en Blazor?
En Blazor, un componente es la herramienta que se usa para crear lo que ve el usuario final. Por ejemplo, en una página web sería la home, otra de sus URL, uno de los elementos desplegables…
Los componentes de Razor se pueden usar en aplicaciones de Blazor, lo que los convierte automáticamente en componentes de Blazor. Razor es una sintaxis de código basada en .NET y destinada a crear páginas web y que usa extensiones como .cshtml o .razor.
Blazor vs. React o Angular
Tanto Blazor como React y Angular son frameworks de código abierto. Blazor se sirve de C#, mientras que React y Angular usan TypeScript. El primero es de Microsoft, mientras que React fue creado por Facebook y Angular lo creó Google.
Usar uno u otro depende de las necesidades del desarrollador: el lenguaje de programación que prefiere, la comunidad de desarrolladores que hay detrás (React y Angular tienen más años que Blazor)… Pero no cabe duda de que con Blazor es muy fácil utilizar toda la potencia de WebAssembly para crear aplicaciones web que aprovechan todos los recursos del sistema.
Vanilla wasm project
En primer lugar, para cada proyecto de ensamblaje web, necesitarás un csproj que utilice el framework .net6, el SDK Microsoft.NET.Sdk.BlazorWebAssembly para habilitar los objetivos wasm y las referencias a los nugets WebAssembly y DevServer.
Además, tenemos que añadir una clase estática Program.cs con un método Main que cree el constructor de hosts, y una carpeta wwwroot con un archivo index.html que llame al punto de entrada blazor.webassembly.js. Por último, para publicar algo en la consola, lo puedes llamar Console.WriteLine en el método main. Para habilitar el depurador, un lauchSettings.json con la propiedad «inspectUri» debe estar en la carpeta de propiedades.
Si parece un poco complicado, no te preocupes: lo entenderás mucho mejor viendo el ejemplo de la consola. Puedes usar esta muestra como tu plantilla de Web Assembly por defecto. Otra forma fácil sería, simplemente, crear una aplicación Blazor WebAssembly desde Visual Studio 2022, y luego eliminar todos los archivos razor, además de quitar de Program.cs las líneas que agregan RootComponents y servicios de alcance.
Una vez que todo esté en su lugar, pon un punto de interrupción en Program.cs y ejecuta el depurador utilizando el nombre del perfil del Proyecto, o el perfil IIS Express (te recomendamos el primero ya que parece ser más rápido). ¡Tu aplicación web se detendrá en el punto de interrupción en el Código .Net 6/WebAssembly!
Javascript <-> .Net interaction
La interacción de ida y vuelta entre Javascript y .Net es posible utilizando el JSRuntime expuesto por el host de Blazor. Sin embargo, fuera de la caja solo se pueden invocar funciones globales de javascript desde .Net. Por lo tanto, hemos construido una envoltura JSRuntime (mira la muestra jsinteraction) que expone una API como la que estaba disponible anteriormente en mono. Permite no solo invocar funciones js sino también manipular «JSObjects» como obtener/configurar propiedades o añadir escuchadores de eventos que llaman de nuevo a .Net.
Aunque esta flexibilidad es un gran punto a favor, tiene un precio: es bastante lento. Consulta la siguiente sección para saber más.
Fast Javascript interaction: Linking to a native library, and callback
La funcionalidad disponible en los ejemplos anteriores es muy buena, pero no es suficiente para construir aplicaciones de alto rendimiento. Por ejemplo, cuando añades un receptor de eventos cuyo callback necesita acceder a propiedades y métodos de js, se realizan múltiples llamadas entre js y .net cada vez que se acciona. Esto es especialmente perjudicial en eventos que accionan a menudo, como “mousemove”.
Para resolver esto, al igual que muchos otros problemas como la interacción con bibliotecas nativas del navegador como WebGL, OpenAL o WebXR, la mejor opción aquí es crear una biblioteca personalizada de C++ o Rust y luego llamarla usando P/Invoke.
Consulta el ejemplo de consola nativa para saber cómo compilar una librería C++ usando emscripten, que además hace un callback a .Net muy rápido.
Esta es la forma óptima de trabajar con el navegador, pero su arquitectura es un poco más compleja. Recomendamos mantener la envoltura JSRuntime para los algoritmos que no son en tiempo real, como las inicializaciones o los eventos esporádicos, y dejar el enfoque de las bibliotecas nativas para el resto, como las operaciones que ocurren dentro de un bucle de dibujo.
Utilizar Virtual File System
El SDK de Blazor no soporta (al menos por ahora) trabajar con el sistema de archivos virtual. Esto significa que hay que cargar los activos web en un sistema de archivos virtual de Javascript, al que luego se puede acceder desde .Net simplemente utilizando la API System.File, como si se estuviera en un sistema operativo real. Esto tiene muchas ventajas, como la reutilización de código .Net o bibliotecas que fueron construidas originalmente para trabajar para Windows o cualquier otro sistema operativo.
Afortunadamente, hemos implementado un sistema VFS que indica a tu aplicación de ensamblaje web que cargue los archivos descritos en tu csproj en el sistema de archivos virtual, para que tu aplicación los utilice posteriormente sin problemas. Comprueba el código fuente en el ejemplo del sistema de archivos.
Producción: Compresión Brotli/Gzip
Los tiempos de carga son muy importantes en todas las aplicaciones. Como probablemente sabes, en las aplicaciones web el tamaño de los archivos es crítico para el tiempo de carga. Los objetivos del SDK de Blazor WebAssembly ya nos permiten comprimir nuestros activos web usando GZip o Brotli, pero no proporcionan una solución out-of-the-box para servir esos archivos cuando el cliente los pide.
Afortunadamente, hemos encontrado una solución sencilla: crear un servidor ASP.net «vacío» y configurarlo para que les sirva a archivos comprimidos automáticamente cuando exista una versión comprimida de un archivo en el servidor. Comprueba el código fuente completo en el ejemplo del servidor del sistema de archivos.
Apuntes finales
Esperamos que estos ejemplos te resulten útiles y te ayuden a potenciar tus aplicaciones wasm. Todo este trabajo se enmarca en los esfuerzos realizados para llevar WaveEngine a .Net6 en web. Con estos bloques de construcción, hemos sido capaces de renderizar con WebGL a un gran rendimiento, así como con otras tecnologías como OpenAL o WebXR.
Los repositorios de muestras ya tienen una muestra de WebXR que estamos usando como sandbox para probar la tecnología, y quizás pronto añadiremos las de WebGL y OpenAL en un nuevo artículo. ¡Mantente informado para saber más!