# Lifecycle y ownership

Registro, mount, props vivas, desmontaje, keep-alive y limpieza de recursos por aplicación.

Consulta las versiones, cambios y requisitos en [Framework 4.0 y CLI 1.2](/es/docs/guides/releases/).


<span class="section-anchor" id="section-1"></span>

## Registro no equivale a montaje

`wu.init` registra metadatos y permisos. `wu.define` registra funciones. `wu.mount` resuelve una app, prepara su contenedor, carga su entry si falta la definición y ejecuta el renderer. El nombre une esos tres pasos.

```js
import { wu } from 'wu-framework';

let stop = () => {};
let abort = null;

wu.define('activity', {
  mount(container, { wu: appWu }) {
    abort = new AbortController();
    const output = document.createElement('output');
    const refresh = () => {
      output.textContent = String(appWu.store.get('activity.count') ?? 0);
    };
    container.replaceChildren(output);
    stop = appWu.store.on('activity.count', refresh);
    refresh();
    container.addEventListener('click', () => {
      appWu.emit('activity:opened', { source: 'counter' });
    }, { signal: abort.signal });
  },
  update(container, props) {
    container.setAttribute('aria-label', props.label ?? 'Actividad');
  },
  unmount(container) {
    stop();
    abort?.abort();
    container.replaceChildren();
  },
});
```

La app necesita solicitudes y grants para leer `activity.count` y emitir `activity:opened`. El ejemplo conserva la facade recibida; el import de `wu` se utiliza para registrar el lifecycle.

<span class="section-anchor" id="section-2"></span>

## Slots disponibles

| Slot | Firma / uso |
| --- | --- |
| `mount` | `(container, { appName, wu })`; obligatorio |
| `unmount` | `(container, context)`; libera renderer, listeners y recursos propios |
| `update` | `(container, nextProps)`; actualización sin desmontar |
| `hydrate` | `(container, { appName, wu, props })`; adopta marcado SSR |
| `deactivate` | `(container)`; se invoca antes de ocultar con keep-alive |
| `activate` | `(container)`; reactiva una app preservada |

Los slots admiten promesas. Rechazar una promesa en `mount` permite que el core limpie el intento y consulte la recuperación. Ocultar un error sin renderizar nada hace que WU pueda considerar exitoso el montaje; lanza errores cuando realmente no puedas montar.

<span class="section-anchor" id="section-3"></span>

## Secuencia observable

El montaje ejecuta `beforeLoad`, el hook de plugin `beforeMount`, crea el sandbox, ejecuta `afterLoad`, obtiene o carga la definición, ejecuta `beforeMount`, espera estilos y llama `hydrate` o `mount`. Luego registra el estado montado y ejecuta hooks posteriores.

`afterLoad` se ejecuta después de crear el sandbox y antes de resolver/cargar el lifecycle. Usa `afterMount` cuando necesites reaccionar al árbol ya montado.

El core serializa las transiciones sobre un mismo contenedor. Una app no debe borrar un nodo cuya propiedad ya se transfirió a otra. También existe conteo de referencias y desmontaje diferido para absorber ciclos de integración como React StrictMode. Si varios consumidores comparten una app, cada montaje debe tener un desmontaje correspondiente.

<span class="section-anchor" id="section-4"></span>

## Props vivas

```js
const updated = await wu.update('activity', { label: 'Actividad reciente' });
if (!updated) console.info('La app no aplicó esta actualización');
```

`update` no fabrica un renderer si la app no implementa ese slot. Los adapters React y Vue conservan props y actualizan su raíz sin remount. Las props iniciales se configuran en el registro del adapter o en SSR; `WuLifecycle.mount` no declara `context.props` para el montaje normal.

<span class="section-anchor" id="section-5"></span>

## Ocultar y destruir

```js
await wu.hide('activity');
console.log(wu.isHidden('activity'));
await wu.show('activity');
await wu.unmount('activity', { force: true });
```

`hide` conserva DOM, iframe cuando existe, estado, timers y listeners. La aplicación sigue consumiendo recursos; `deactivate` es el lugar para pausar trabajo propio que no interesa mientras está oculta. `show` restaura visibilidad y ejecuta `activate` sin volver a cargar el entry.

`unmount` resuelve `keepAlive` en este orden: `force` obliga a destruir; de lo contrario prevalece la opción de la llamada, luego la config de la app y finalmente `false`. Desmontar por completo revoca la identidad scoped, listeners y handlers asociados. Una referencia a `context.wu` conservada después del desmontaje falla con `WU_APP_SCOPE_REVOKED`.

`wu.destroy()` limpia el runtime completo. Úsalo cuando el shell deja de existir, no para cambiar una sola sección. `wu.app(name, config)` entrega un wrapper con `mount`, `unmount`, `hide`, `show`, `remount`, `reload`, `verify`, `start`, `stop` y `destroy`.

<span class="section-anchor" id="section-6"></span>

## Wrapper: registro diferido y concurrencia

`wu.app(name, config)` agrupa la identidad, configuración y destino de una aplicación. Crear el wrapper conserva esos datos; el registro efectivo ocurre al montar, a través del core y de su validación de manifest y permisos. Si el core aún no se ha iniciado, el wrapper inicia esa configuración antes de montar.

```js
const widget = wu.app('widget', {
  url: '/apps/widget',
  container: '#widget-slot',
  framework: 'vue',
  grants: [],
});
await widget.mount();
console.log(widget.isMounted, widget.info.container);
await widget.hide();
await widget.show();
await widget.unmount({ force: true });
```

La URL debe servir el manifest de `widget`. `mount(selector)` permite elegir otro destino y lo conserva para llamadas posteriores sin argumento. Los métodos de montaje, desmontaje, visibilidad, remount y reload resuelven al mismo wrapper, para poder inspeccionar su estado al terminar.

| Miembro | Uso |
| --- | --- |
| `mount(selector?)` / `start(selector?)` | Registrar si hace falta y montar sobre un selector string |
| `unmount(options?)` / `stop()` | Desmontar; stop utiliza las opciones por defecto |
| `hide()` / `show()` | Conservar y reactivar una app montada |
| `remount(selector?)` | Forzar desmontaje y volver a montar |
| `reload()` | Desmontar, limpiar cachés del loader/manifest disponibles y montar de nuevo |
| `isMounted` / `isHidden` | Consultar estado del wrapper y del core |
| `info` | Nombre, URL, selector, montaje y status |
| `verify()` | Inspeccionar registro, selector y Shadow DOM del contenedor |

Si otro `init` está en vuelo, el wrapper lo espera y registra su app después si aún falta. Dos mounts concurrentes sobre **el mismo wrapper** comparten una promesa de registro; cada montaje sigue participando en el ownership/conteo de referencias del core. Un fallo libera esa promesa para permitir un reintento. Un registro existente del host se conserva, sin sobrescribir sus grants con los de otro wrapper.

`reload()` no invalida el caché nativo de módulos ESM del navegador ni garantiza descargar una versión diferente del mismo URL. Para actualizar una versión desplegada, publica entries con identidad de build y coordina su carga desde el shell.

`verify().container.hasContent` inspecciona los hijos del ShadowRoot del slot; un resultado false no demuestra que un renderer con ownership propio o light DOM esté vacío. `WuApp.destroy()` elimina la app del mapa público, pero no limpia todos los registros de manifest asociados. Para retirar un renderer usa `unmount`; para reinicializar el shell completo usa `wu.destroy()` y un nuevo `init`, en lugar de tratar el destroy del wrapper como una operación general de sustitución de identidad.

<span class="section-anchor" id="section-7"></span>

## Referencia de implementación

Slots: `src/index.d.ts`; definición: `src/core/wu-core.js`; montaje: `src/core/wu-core.js`; actualización: `src/core/wu-core.js`; teardown: `src/core/wu-core.js`; keep-alive: `src/core/wu-core.js`; wrapper: `src/core/wu-app.js`. Pruebas: `tests/unit/keep-alive.test.js`, `tests/unit/props-channel.test.js`, `tests/unit/shared-container-navigation.test.js`, `tests/unit/scoped-events-teardown.test.js`.