Ir al contenido

Adapter de Vue

El adapter de Vue registra un componente raíz de Vue 3 como micro-app. Soporta props vivas e hidratación SSR, pero —a diferencia de React— ambas son condicionales a lo que exponga tu build de Vue, y el adapter solo anuncia el slot del ciclo de vida cuando la capacidad realmente está.

Ventana de terminal
npm install wu-framework vue
// src/main.ts — la micro-app
import { wuVue } from 'wu-framework/adapters/vue';
import App from './App.vue';
wuVue.register('catalog', App);
import { createPinia } from 'pinia';
import { router } from './router';
await wuVue.register('catalog', App, {
props: { apiUrl: 'https://api.example.com' },
setup: (app) => {
app.use(createPinia());
app.use(router);
app.component('MoneyLabel', MoneyLabel);
},
standaloneContainer: '#app',
onMount: (container, app) => console.log('montado', app.version),
onUnmount: (container, app) => console.log('desmontando'),
});
Opción Tipo Por defecto Notas
props object {} Se pasan como props raíz. Se hacen reactivas cuando es posible — ver abajo.
setup function null (app) => void. Se ejecuta después de createApp(), antes de mount(). Tu punto de enganche para plugins, router, componentes globales y directivas.
standalone boolean true Monta directamente cuando no se encuentra un shell.
standaloneContainer string '#app' Selector usado en modo standalone. Ojo: #app, no el #root de React.
onMount function null (container, app) => void — recibe la instancia de la app Vue.
onUnmount function null (container, app) => void — se ejecuta antes de app.unmount().

Devuelve Promise<boolean>.

setup(app) es el único sitio donde instalar cualquier cosa a nivel de app. Se ejecuta en cada montaje (y en cada hidratación), así que una micro-app reubicada o remontada obtiene una aplicación Vue totalmente configurada, no una a medio cablear.

El adapter también provee dos inyectables a todos los componentes del árbol:

<script setup>
import { inject } from 'vue';
const appName = inject('wuAppName'); // 'catalog'
const wu = inject('wuInstance'); // la instancia de Wu, o null
</script>

ensureVue() comprueba primero window.Vue y luego recurre a un import('vue') dinámico. Si ninguno resuelve, register() devuelve false.

Props vivas — condicionales a Vue.reactive

Sección titulada «Props vivas — condicionales a Vue.reactive»

El adapter envuelve tus props en Vue.reactive() en el momento del registro, y mutar ese objeto se propaga al componente raíz sin remontar:

await wu.update('catalog', { apiUrl: 'https://staging.example.com' });

Si Vue.reactive no está disponible en el namespace de Vue resuelto, el adapter usa un objeto plano y no anuncia update en absoluto. Ese es el resultado honesto: asignar a un objeto plano no dispararía ninguna reactividad, así que wu.update() parecería funcionar sin hacer nada. Sin el slot, wu.update() no hace nada de forma visible.

Hidratación SSR — condicional a createSSRApp

Sección titulada «Hidratación SSR — condicional a createSSRApp»

Cuando Vue 3 expone createSSRApp, el adapter anuncia hydrate. wu-core lo llama en lugar de mount cuando el contenedor vino de wu-framework/server, y createSSRApp(...).mount(container) adopta el DOM existente en vez de recrearlo: sin parpadeo, y lo que el usuario ya está viendo se queda en pantalla.

Las props del servidor se fusionan sobre las props reactivas antes de hidratar: son las props que produjeron el markup.

Si la hidratación lanza, el adapter lo registra y recurre a un mountApp() normal. En Vue 2 (sin createSSRApp) el slot hydrate simplemente no está y el markup del servidor se monta encima.

Wu monta las micro-apps dentro del Shadow DOM por defecto, pero la inyección de estilos de Vue en modo desarrollo escribe etiquetas <style data-vite-dev-id> en document.head — fuera del límite del shadow, donde no pueden alcanzar tus componentes.

Al montar, el adapter sube desde el contenedor buscando un ShadowRoot. Cuando lo encuentra, espera unos 100 ms a que Vue inyecte sus estilos y luego clona dentro del shadow root cada style[data-vite-dev-id] cuya ruta de id contenga /<appName>/ (o \<appName>\ en Windows).

Vue es el único adapter que exporta un componente listo para usar en lugar de una factoría:

<script setup>
import { WuSlot } from 'wu-framework/adapters/vue';
</script>
<template>
<WuSlot
name="catalog"
url="http://localhost:3001"
fallback-text="Cargando catálogo…"
@load="onLoad"
@error="onError"
@mount="onMount"
@unmount="onUnmount"
/>
</template>
Prop Tipo Obligatoria Notas
name String Nombre de la micro-app.
url String URL de la app.
appName String no Sustituye a name para la búsqueda en Wu.
fallbackText String no Por defecto 'Loading...'.

Emite load, error, mount y unmount.

WuSlot usa una función de render, no un template, así que funciona con el build runtime-only de Vue. Resuelve h() desde el namespace de Vue del adapter o desde window.Vue; si ninguno está accesible, registra un error explícito en lugar de renderizar null para siempre en silencio.

import { createApp } from 'vue';
import * as Vue from 'vue';
import { wuVuePlugin } from 'wu-framework/adapters/vue';
const app = createApp(Shell);
app.use(wuVuePlugin, { Vue }); // { Vue } es opcional — ver abajo
app.mount('#app');

El plugin:

  • registra WuSlot globalmente,
  • provee wu para inyección,
  • añade $wu, $wuEvents y $wuStore(ns) a globalProperties,
  • envuelve app.unmount para que las suscripciones de $wuEvents se limpien.

Pasa { Vue } cuando 'vue' no sea un especificador simple resoluble en tu build y no exista window.Vue: esa es la única forma de que WuSlot.render() obtenga h().

<script setup>
import { onUnmounted } from 'vue';
import { useWuEvents } from 'wu-framework/adapters/vue';
const { emit, on, once, off, cleanup } = useWuEvents();
on('user:login', (e) => console.log(e.data));
onUnmounted(cleanup);
</script>
<script setup>
import { onUnmounted } from 'vue';
import { useWuStore } from 'wu-framework/adapters/vue';
const { state, setState, getState, cleanup } = useWuStore('user');
onUnmounted(cleanup);
</script>
<template>
<p>{{ state.value?.name }}</p>
</template>

state es un ref cuando Vue.ref está disponible, y un objeto plano { value: null } en caso contrario. setState(path, value) escribe en <namespace>.<path>. Llama a cleanup() al desmontar.

<script setup>
import * as Vue from 'vue';
import { createUseWuAI } from 'wu-framework/adapters/vue';
const useWuAI = createUseWuAI(Vue);
const { messages, isStreaming, error, send, clear } = useWuAI({ namespace: 'support' });
</script>

createUseWuAI(Vue) devuelve un composable cuyos messages, isStreaming y error son refs de Vue: reactivos en los templates. El export simple useWuAI() no necesita el namespace de Vue pero devuelve valores no reactivos; úsalo desde JavaScript normal, no desde un template.

Ambos llaman a wu.ai.send(). Vue no tiene variante de streaming: para salida token a token, itera wu.ai.stream() tú mismo.

import {
wuVue, // export por defecto
register,
WuSlot,
useWuEvents,
useWuStore,
wuVuePlugin,
createUseWuAI,
useWuAI,
getWuInstance,
waitForWu,
} from 'wu-framework/adapters/vue';

Desde el barrel, estos son registerVue, createVueUseWuAI y useVueWuAI.