Ir al contenido

Tu primera micro-app

Una micro-app de Wu es una app corriente más dos cosas:

  1. un manifiesto wu.json servido en la raíz de la URL de la app, y
  2. una llamada a wu.define() (o al register() de un adaptador) cuando se ejecuta su bundle.

Nada más cambia. Misma herramienta de build, mismo servidor de desarrollo, mismas dependencias.

La forma agnóstica al framework:

src/main.js
wu.define('cart', {
mount(container) {
container.innerHTML = '<h1>Carrito</h1>';
},
unmount(container) {
container.innerHTML = '';
},
});

container es un <div> dentro de un shadow root que creó el shell por ti. Renderiza dentro como prefieras.

Con un adaptador, lo mismo en una línea:

src/main.jsx
import { wuReact } from 'wu-framework/adapters/react';
import App from './App.jsx';
wuReact.register('cart', App);
// src/main.js (Vue)
import { wuVue } from 'wu-framework/adapters/vue';
import App from './App.vue';
wuVue.register('cart', App);

El adaptador escribe la llamada a wu.define(), conecta el mount/unmount de tu framework y añade los slots opcionales update y hydrate cuando el framework puede soportarlos.

public/wu.json
{
"name": "cart",
"entry": "src/main.jsx"
}

Debe ser accesible en <appUrl>/wu.json. En Vite, todo lo que hay en public/ se sirve desde la raíz, así que public/wu.jsonhttp://localhost:5173/wu.json.

Ese es todo el contrato de una micro-app. El shell obtiene wu.json, carga entry, tu bundle llama a define() y el shell ejecuta el ciclo de vida.

entry se resuelve de forma relativa a la URL de la app. Si empieza por un prefijo de carpeta conocidosrc/, dist/, public/, build/, assets/, lib/ o es/ — Wu lo usa directamente:

{ "entry": "src/main.jsx" } // → <url>/src/main.jsx (desarrollo)
{ "entry": "assets/index-a1b2.js" }// → <url>/assets/index-a1b2.js (compilado)

Cualquier otra cosa dispara un sondeo de 8 rutas: Wu lanza peticiones HEAD en paralelo a src/, la raíz, dist/, public/, build/, assets/, lib/ y es/, y se queda con la primera que responda con algo con pinta de JavaScript. Funciona, pero cuesta ocho peticiones y es el origen del fallo en producción más habitual — mira Errores comunes.

Publica siempre un entry con prefijo en producción. Para un build de Vite eso significa apuntar al archivo con hash en dist/assets/, que puedes generar durante el build:

// vite.config.js — escribe wu.json con el entry real tras cada build
import { writeFileSync } from 'node:fs';
export default {
plugins: [{
name: 'wu-manifest',
writeBundle(_, bundle) {
const entry = Object.values(bundle).find((c) => c.isEntry);
writeFileSync('dist/wu.json', JSON.stringify({
name: 'cart',
entry: `assets/${entry.fileName.split('/').pop()}`,
styleMode: 'shared',
}, null, 2));
},
}],
};

Los adaptadores incluyen un respaldo standalone. Si el bundle se ejecuta en una página donde no existe ningún runtime de Wu, register() espera un momento (3 s por defecto), se rinde y monta el componente en #root:

wuReact.register('cart', App, {
standalone: true, // por defecto
standaloneContainer: '#root' // por defecto
});

Así http://localhost:5173 sigue funcionando como una app normal durante el desarrollo, y el mismo bundle se convierte en micro-app cuando un shell lo carga. Pon standalone: false si quieres que la app quede inerte fuera de un shell.

register(appName, Component, options) acepta:

Opción Por defecto Significado
props {} Props iniciales que se pasan al componente
standalone true Monta en #root cuando no hay shell
standaloneContainer '#root' Selector para el montaje standalone
onMount null Callback (container) => void tras el montaje
onUnmount null Callback (container) => void antes del desmontaje

Las props se pueden enviar más tarde sin remontar mediante wu.update() — el adaptador expone un slot update cuando su framework puede volver a renderizar en el sitio.

Dentro de la app, wu está disponible globalmente (y dentro de los sandboxes strict/eval como una fachada restringida y congelada con la misma superficie pública):

// publicar
wu.emit('cart:item-added', { sku: 'SKU-42' });
wu.store.set('cart.total', 142.5);
// suscribirse
wu.on('user:logout', () => clearCart());
wu.store.on('user.*', (value, path) => console.log(path, value));

Mira Comunicación.

Lista de comprobación antes de darlo por terminado

Sección titulada «Lista de comprobación antes de darlo por terminado»
  • wu.json se sirve en <appUrl>/wu.json también en producción
  • entry usa un prefijo de carpeta conocido
  • El servidor de la app envía Access-Control-Allow-Origin para el origen del shell
  • El JavaScript se sirve con un content type de JavaScript (no text/html)
  • unmount realmente desmonta — Wu limpia los temporizadores y listeners que puede rastrear, pero la raíz de tu framework es cosa tuya

Añade un shell que la aloje.