Adapters de Alpine, HTMX y Stimulus
Alpine, HTMX y Stimulus comparten una filosofía: el HTML es el componente.
El comportamiento se declara con atributos y un runtime recorre el DOM para
cablearlo. Por eso los tres adapters tienen la misma forma —un objeto config
con un string template— y su mount es esencialmente “inyecta el HTML y luego
llama a la función de activación del runtime sobre ese subárbol”.
| Alpine | HTMX | Stimulus | |
|---|---|---|---|
| Subpath | wu-framework/adapters/alpine |
wu-framework/adapters/htmx |
wu-framework/adapters/stimulus |
| 2.º argumento | objeto appConfig |
objeto config |
objeto config |
| Config obligatoria | template |
template |
template, controllers |
| El runtime se busca en | window.Alpine |
window.htmx |
import('@hotwired/stimulus') dinámico |
| Activación | Alpine.initTree(container) |
htmx.process(container) |
Application.start(container) |
| Props vivas | no | no | no |
| Hidratación SSR | no | no | no |
| Contenedor standalone | #app |
#app |
#app |
Los tres aceptan las mismas opciones: onMount, onUnmount, standalone y
standaloneContainer; y los tres lanzan de forma síncrona si falta
template o no es un string.
Alpine.js
Sección titulada «Alpine.js»npm install wu-frameworkAlpine debe estar en la página como window.Alpine — el build de CDN o
window.Alpine = Alpine desde tu bundle.
import { wuAlpine } from 'wu-framework/adapters/alpine';
wuAlpine.register('counter', { template: ` <div x-data="app"> <span x-text="count"></span> <button @click="count++">+</button> </div> `, data: () => ({ count: 0 }),}, { onMount: (container) => console.log('Montado'), onUnmount: (container) => console.log('Desmontado'),});register(appName, appConfig, options)
Sección titulada «register(appName, appConfig, options)»Clave de appConfig |
Tipo | Notas |
|---|---|---|
template |
string |
Obligatorio. HTML con directivas de Alpine. |
data |
function |
Se registra como Alpine.data('app', fn). |
El montaje inyecta el template, llama a Alpine.data('app', data) si pasaste
una función data, y luego a Alpine.initTree(container). El desmontaje llama
a Alpine.destroyTree(container) cuando está disponible y después vacía el
contenedor.
Si falta window.Alpine, el adapter avisa —
template inserted but not initialized — y deja el HTML inerte en su sitio en
lugar de hacer fallar el montaje.
import { wuHtmx } from 'wu-framework/adapters/htmx';
wuHtmx.register('inbox', { template: ` <div> <button hx-get="/messages" hx-target="#list" hx-swap="innerHTML"> Actualizar </button> <ul id="list"></ul> </div> `, baseUrl: 'https://api.example.com',}, { onMount: (container) => console.log('app HTMX lista'),});register(appName, config, options)
Sección titulada «register(appName, config, options)»Clave de config |
Tipo | Notas |
|---|---|---|
template |
string |
Obligatorio. HTML con atributos hx-*. |
baseUrl |
string |
Opcional. Prefija los endpoints HTMX relativos a la raíz. |
Qué hace realmente baseUrl
Sección titulada «Qué hace realmente baseUrl»Tras inyectar el template, el adapter recorre hx-get, hx-post, hx-put,
hx-patch y hx-delete y reescribe cualquier valor que empiece por / a
baseUrl + valor.
baseUrl: 'https://api.example.com'// hx-get="/messages" → "https://api.example.com/messages"// hx-get="messages" → sin cambios (no es relativo a la raíz)// hx-get="https://x.test/y" → sin cambios (es absoluto)Esto importa porque el HTML de un micro-frontend se ejecuta en el origen del
shell. Un hx-get="/messages" relativo a la raíz iría, si no, al servidor
del shell y no al backend de tu micro-app.
La activación es window.htmx.process(container). Sin window.htmx el adapter
avisa — template inserted but hx-* attributes will not function — y continúa.
El desmontaje simplemente borra el subárbol, lo que se lleva por delante todos los listeners gestionados por HTMX.
Stimulus
Sección titulada «Stimulus»npm install wu-framework @hotwired/stimulusimport { Controller } from '@hotwired/stimulus';import { wuStimulus } from 'wu-framework/adapters/stimulus';
class SearchController extends Controller { static targets = ['input', 'results'];
query() { this.resultsTarget.textContent = `Buscando ${this.inputTarget.value}…`; }}
wuStimulus.register('search', { controllers: { search: SearchController }, template: ` <div data-controller="search"> <input data-search-target="input" data-action="input->search#query"> <p data-search-target="results"></p> </div> `,});register(appName, config, options)
Sección titulada «register(appName, config, options)»Clave de config |
Tipo | Notas |
|---|---|---|
template |
string |
Obligatorio. HTML con data-controller / data-action. |
controllers |
object |
Obligatorio y no vacío. Mapa de identificador → clase Controller. |
Ambos se validan de inmediato y lanzan si faltan:
[WuStimulus] template (HTML string) is required for <appName>[WuStimulus] controllers (object of name -> Controller class) is required for <appName>Una Application acotada por micro-app
Sección titulada «Una Application acotada por micro-app»A diferencia de un montaje típico de Stimulus con una única Application
global, este adapter llama a Application.start(container) — una Application
acotada al contenedor. Su MutationObserver solo vigila tu subárbol, así que
dos micro-apps de Stimulus en la misma página no pueden verse ni conectarse a
los controladores de la otra.
El orden importa en mount: la Application arranca y todos los controladores se
registran antes de inyectar el template. El observer de Stimulus los conecta
entonces conforme aparecen los nodos data-controller.
@hotwired/stimulus se carga con un import() dinámico, para mantenerlo fuera
de la ruta crítica. Si el import falla, el contenedor recibe
Failed to load Stimulus runtime y el error se vuelve a lanzar.
El desmontaje llama a application.stop() y vacía el contenedor.
Exports
Sección titulada «Exports»Cada uno de los tres exporta la misma superficie mínima:
import { wuAlpine, register, getWuInstance, waitForWu } from 'wu-framework/adapters/alpine';import { wuHtmx } from 'wu-framework/adapters/htmx';import { wuStimulus } from 'wu-framework/adapters/stimulus';Desde el barrel: registerAlpine, registerHtmx, registerStimulus.
Si necesitas más que un template —estado de componente, estructura basada en
clases, un setState programático— mira el
adapter de Vanilla, que cubre el mismo territorio sin
build con una superficie mucho mayor.
