Astro.js: Un nuevo paradigma en Desarrollo Web
Astro.js no es solo una nueva herramienta más para el Desarrollo Web, es un cambio de juego que viene a redefinir la...
El desarrollo web moderno exige velocidad, seguridad y flexibilidad. Rust y WebAssembly han surgido como herramientas poderosas para cumplir con esas demandas, permitiendo a los desarrolladores crear componentes web reutilizables que destacan por su rendimiento. En esta guía práctica aprenderás cómo combinar estas tecnologías en tus proyectos web.
Tabla de contenidos
Como bien sabes, el sector del desarrollo web evoluciona constantemente, y los desarrolladores buscan nuevas formas de crear aplicaciones más robustas y eficientes.
En este contexto, Rust y WebAssembly (Wasm) surgen como una alternativa para construir componentes web de alto rendimiento, con la premisa de ser fáciles de reutilizar.
En esta guía práctica, te explicaremos paso a paso cómo crear componentes web reutilizables usando Rust y WebAssembly. Además, analizaremos las ventajas de cada tecnología, así como los beneficios que aportan y las herramientas necesarias para su implementación.
WebAssembly, o Wasm, es un formato de código binario optimizado para ejecutarse en navegadores web, con un alto grado de rendimiento, ha sido desarrollado por el W3C, World Wide Web Consortium.
Este formato permite el desarrollo de aplicaciones con lenguajes de programación como Rust o C++. Los programadores no desarrollan directamente en WebAssembly, sino que escriben en el lenguaje de su elección, que luego se compila en bytecode WebAssembly.
Desde hace mucho tiempo en el entorno web solo disponíamos de JavaScript para utilizarlo de forma nativa, pero ahora con Wasm lo complementa proporcionando la velocidad y eficiencia necesarias para tareas de cómputo intensivo.
Mientras que JavaScript sigue realizando la manipulación del DOM y la lógica de la interfaz de usuario. Además, WebAssembly es compatible con los principales navegadores del mercado.
Rust se ha convertido en una opción popular para el desarrollo de WebAssembly debido a estos aspectos clave:
Para crear un componente web reutilizable con Rust y Wasm, comenzamos definiendo una estructura en Rust que representará nuestro componente. Aquí tienes un ejemplo básico:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct MyComponent {
value: i32,
}
#[wasm_bindgen]
impl MyComponent {
pub fn new() -> MyComponent {
MyComponent { value: 0 }
}
pub fn increment(&mut self) {
self.value += 1;
}
pub fn get_value(&self) -> i32 {
self.value
}
}
En este ejemplo desarrollado con Rust y WebAssembly, cada parte cumple un rol específico para que el componente funcione de forma eficiente y se pueda utilizar en una aplicación web.
A continuación, te explico el cometido de cada anotación:
use wasm_bindgen::prelude::*;
Esta línea importa el módulo wasm_bindgen
, que es fundamental para interactuar con WebAssembly desde JavaScript. La macro #[wasm_bindgen]
permite que Rust exponga funciones y estructuras a WebAssembly de manera que puedan ser utilizadas desde el código JavaScript del navegador.
#[wasm_bindgen]
Esta anotación se coloca encima de las estructuras y funciones que queremos hacer accesibles desde JavaScript. Sin #[wasm_bindgen]
, el código Rust no sería directamente accesible en WebAssembly, y por lo tanto, JavaScript no podría utilizar el componente.
pub struct MyComponent
Aquí definimos la estructura MyComponent
, que es la parte central de nuestro componente. En Rust, una struct
es una forma de agrupar datos; en este caso, estamos creando una estructura que contiene un solo campo llamado value
, de tipo i32
(un número entero de 32 bits).
La palabra clave pub
indica que esta estructura es pública y accesible desde fuera del módulo en el que se define, permitiendo que sea visible y utilizable desde JavaScript a través de WebAssembly.
Para utilizar este código, tienes que compilarlo a WebAssembly (Wasm) usando wasm-bindgen
y luego interactuar con él en un entorno JavaScript.
Para ello tienes que seguir estos pasos:
1. Configura el proyecto en Rust
Debes de tener el compilador de Rust y las herramientas necesarias instaladas. Si no es así puedes obtenerla ejecutando el siguiente comando:
cargo install wasm-pack
2. Configura el arhivo Cargo.toml
En el archivo Cargo.toml
, añade las dependencias necesarias para wasm-bindgen
:
[dependencies]
wasm-bindgen = "0.2"
3. Compilar el proyecto a WebAssembly
Utiliza wasm-pack
para compilar el proyecto a un paquete compatible con JavaScript:
wasm-pack build --target web
Esto generará una carpeta pkg
con los archivos necesarios para usar tu módulo en JavaScript, incluidos el archivo .wasm
y un archivo .js
que actúa como una interfaz entre JavaScript y WebAssembly.
4. Importa y usa el paquete en JavaScript
En tu archivo JavaScript (por ejemplo, index.js
), importa y utiliza el módulo Wasm:
// Importa el módulo generado por wasm-pack
import init, { MyComponent } from "./pkg/tu_paquete";
async function run() {
// Inicializa el módulo de Wasm
await init();
// Crea una instancia de `MyComponent`
const myComponent = new MyComponent();
// Llama a los métodos
console.log(myComponent.get_value()); // Muestra 0
myComponent.increment();
console.log(myComponent.get_value()); // Muestra 1
}
run();
5. Configura un servidor local para servir el archivo .wasm
Para evitar problemas con CORS, utiliza un servidor local, como serve
, para probar tu proyecto:
npx serve .
Para integrar nuestro componente Rust/Wasm con HTML y CSS, utilizamos JavaScript como pasarela de unión.
Aquí te muestro un ejemplo de cómo funciona la integración:
<div id="my-component"></div>
<script type="module">
import init, { MyComponent } from "./pkg/my_component.js";
async function run() {
await init();
const component = MyComponent.new();
const element = document.getElementById("my-component");
element.innerHTML = `
<button id="increment">Incrementar</button>
<p>Valor: <span id="value"></span></p>
`;
const valueSpan = element.querySelector("#value");
const incrementButton = element.querySelector("#increment");
function updateValue() {
valueSpan.textContent = component.get_value();
}
incrementButton.addEventListener("click", () => {
component.increment();
updateValue();
});
updateValue();
}
run();
</script>
Este fragmento de código muestra cómo integrar un componente en Rust y WebAssembly en una página HTML para crear un pequeño contador interactivo. A continuación, analizamos cada sección para comprender como funciona cada una de las secciones.
<div id="my-component"></div>
Este div
actuará como el contenedor en el que se mostrará el componente del contador. JavaScript luego llenará este contenedor con un botón y un elemento de texto para mostrar el valor actual del contador.
<script type="module">
import init, { MyComponent } from './pkg/my_component.js';
Este código importa el módulo WebAssembly generado a partir del código Rust (en el archivo my_component.js
) y la función init
, que inicializa el módulo Wasm en JavaScript. También se importa MyComponent
, que es el componente definido en Rust.
La función run
es una función asincrónica que realiza varios pasos para inicializar y configurar el componente:
await init();
await init()
inicializa el módulo WebAssembly. Esto carga el código Wasm y permite utilizar MyComponent
como un objeto accesible en JavaScript.
Para encapsular aún más nuestro componente y evitar conflictos de estilos, podemos utilizar el Shadow DOM:
const shadow = element.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
</style>
<button id="increment">Incrementar</button>
<p>Valor: <span id="value"></span></p>
`;
const valueSpan = shadow.querySelector("#value");
const incrementButton = shadow.querySelector("#increment");
El Shadow DOM es especialmente útil cuando se desarrollan Web Components o componentes reutilizables que podrían ser usados en diferentes proyectos o en entornos con estilos variados.
Al encapsular sus estilos y estructura, cada componente puede mantener una apariencia y comportamiento consistentes, sin interferencias.
El Shadow DOM permite encapsular completamente los estilos y la estructura de un componente dentro de un “DOM de sombra”, separado del DOM principal de la página.
Esto se traduce, en que el contenido y los estilos del componente están aislados y no pueden afectar ni ser afectados por el estilo o estructura del resto de la página.
wasm-bindgen
es una herramienta que facilita la comunicación entre Rust y JavaScript. Permite generar automáticamente el código JavaScript necesario para interactuar con las funciones y estructuras de Rust compiladas a WebAssembly.
Para usar wasm-bindgen
, añade este fragmento de código al archivo Cargo.toml
:
[dependencies]
wasm-bindgen = "0.2"
wasm-pack
es una herramienta de línea de comandos que facilita la construcción y empaquetado de proyectos Rust para WebAssembly. Automatiza muchos de los pasos necesarios para compilar Rust a Wasm y generar los archivos JavaScript y TypeScript correspondientes.
Para instalar wasm-pack
, ejecuta en una terminal el siguiente comando:
cargo install wasm-pack
web-sys
y js-sys
son crates que proporcionan bindings a las APIs web y JavaScript respectivamente. Permiten interactuar con el DOM, manejar eventos y utilizar funcionalidades del navegador directamente desde Rust.
Añade estas dependencias al archivo Cargo.toml
:
[dependencies]
web-sys = "0.3"
js-sys = "0.3"
Este es un ejemplo sencillo de creación de un botón interactivo que cambia de color al hacer clic:
use wasm_bindgen::prelude::*;
use web_sys::{Element, HtmlElement};
#[wasm_bindgen]
pub struct ColorButton {
element: HtmlElement,
colors: Vec<String>,
current_color: usize,
}
#[wasm_bindgen]
impl ColorButton {
pub fn new() -> Result<ColorButton, JsValue> {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let element = document.create_element("button")?;
let button = element.dyn_into::<HtmlElement>()?;
button.set_inner_text("Cambiar color");
button.style().set_property("padding", "10px")?;
let colors = vec![
String::from("#ff0000"),
String::from("#00ff00"),
String::from("#0000ff"),
];
button.style().set_property("background-color", &colors[0])?;
Ok(ColorButton {
element: button,
colors,
current_color: 0,
})
}
pub fn attach(&self, parent: &Element) -> Result<(), JsValue> {
parent.append_child(&self.element)?;
Ok(())
}
pub fn on_click(&mut self) -> Result<(), JsValue> {
self.current_color = (self.current_color + 1) % self.colors.len();
self.element.style().set_property("background-color", &self.colors[self.current_color])?;
Ok(())
}
}
Para crear un gráfico dinámico, podríamos utilizar una biblioteca de gráficos en JavaScript que tome una muestra de datos procesados con Rust:
use wasm_bindgen::prelude::*;
use js_sys::Array;
#[wasm_bindgen]
pub struct DataProcessor {
data: Vec<f64>,
}
#[wasm_bindgen]
impl DataProcessor {
pub fn new() -> DataProcessor {
DataProcessor { data: Vec::new() }
}
pub fn add_data(&mut self, value: f64) {
self.data.push(value);
}
pub fn get_processed_data(&self) -> Array {
self.data.iter().map(|&x| JsValue::from(x * 2.0)).collect()
}
}
Este componente podría usarse en conjunto con una biblioteca de gráficos JavaScript para crear visualizaciones aún más complejas, delegando la gestión de los datos a Rust.
La combinación de Rust y WebAssembly ofrece un enfoque muy potente para crear componentes web reutilizables de alto rendimiento.
A medida que la web evoluciona hacia aplicaciones más complejas y exigentes, esta tecnología se posiciona como una solución prometedora para los desafíos de rendimiento y seguridad.
La tendencia hacia la utilización de este stack en el desarrollo web está ganando impulso, de forma que los desarrolladores que dominen estas tecnologías estarán bien posicionados para encontrar nuevos nichos de empleo, donde la eficiencia y rendimiento es el factor primordial.
Si quieres ampliar tus conocimientos en Rust y sus aplicaciones, te recomendamos que leas este artículo completo en OpenWebinars: Tauri: Crea aplicaciones de escritorio con tecnologías web.
También te puede interesar
Astro.js no es solo una nueva herramienta más para el Desarrollo Web, es un cambio de juego que viene a redefinir la...