# 生命周期与所有权

应用的注册、mount、动态 props、卸载、keep-alive 与资源清理。

版本、变更与要求见 [Framework 3.2 与 CLI 1.0](/zh-cn/docs/guides/releases/)。


<span id="registro-no-equivale-a-montaje"></span>

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

## 注册不等于挂载

`wu.init` 注册元数据和权限。`wu.define` 注册函数。`wu.mount` 解析应用、准备容器，在缺少定义时加载 entry，然后执行 renderer。名称将这三个步骤关联起来。

```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();
  },
});
```

应用需要相应请求和 grants 才能读取 `activity.count` 并发射 `activity:opened`。示例保留收到的 facade；导入的 `wu` 用于注册生命周期。

<span id="slots-disponibles"></span>

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

## 可用 slots

| Slot | 签名 / 用途 |
| --- | --- |
| `mount` | `(container, { appName, wu })`；必需 |
| `unmount` | `(container, context)`；释放 renderer、监听器和自有资源 |
| `update` | `(container, nextProps)`；无需卸载即可更新 |
| `hydrate` | `(container, { appName, wu, props })`；接管 SSR 标记 |
| `deactivate` | `(container)`；通过 keep-alive 隐藏前调用 |
| `activate` | `(container)`；重新激活保留的应用 |

slots 支持 Promise。`mount` 中的 Promise 拒绝后，core 可以清理本次尝试并调用恢复机制。吞掉错误却不渲染任何内容，可能让 WU 将挂载视为成功；确实无法挂载时应抛出错误。

<span id="secuencia-observable"></span>

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

## 可观察的执行顺序

挂载依次执行 `beforeLoad`、插件 `beforeMount` hook、创建 sandbox、执行 `afterLoad`、获取或加载定义、执行 `beforeMount`、等待样式，然后调用 `hydrate` 或 `mount`。随后记录已挂载状态并执行后续 hooks。

`afterLoad` 在创建 sandbox 后、解析或加载生命周期前执行。需要对已挂载的树作出响应时，使用 `afterMount`。

core 将同一容器上的状态转换串行化。应用不应删除所有权已转移给其他应用的节点。系统还提供引用计数和延迟卸载，以吸收 React StrictMode 等集成周期。如果多个使用方共享一个应用，每次挂载都应有对应的卸载。

<span id="props-vivas"></span>

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

## 动态 props

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

如果应用未实现 `update` slot，它不会凭空创建 renderer。React 和 Vue adapters 保留 props，并在不重新挂载的情况下更新根节点。初始 props 在 adapter 注册或 SSR 中配置；常规挂载的 `WuLifecycle.mount` 未声明 `context.props`。

<span id="ocultar-y-destruir"></span>

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

## 隐藏与销毁

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

`hide` 保留 DOM、已有 iframe、状态、timers 和监听器。应用仍会占用资源；可在 `deactivate` 中暂停隐藏期间不需要的自有工作。`show` 恢复可见性并执行 `activate`，不会重新加载 entry。

`unmount` 按以下顺序决定 `keepAlive`：`force` 强制销毁；否则先采用本次调用选项，其次是应用配置，最后为 `false`。完整卸载会撤销 scoped 身份及关联的监听器和 handlers。卸载后保留的 `context.wu` 引用会以 `WU_APP_SCOPE_REVOKED` 失败。

`wu.destroy()` 清理整个运行时。应在 shell 结束时使用，而不是只切换一个区域。`wu.app(name, config)` 返回含 `mount`、`unmount`、`hide`、`show`、`remount`、`reload`、`verify`、`start`、`stop` 和 `destroy` 的 wrapper。

<span id="wrapper-registro-diferido-y-concurrencia"></span>

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

## Wrapper：延迟注册与并发

`wu.app(name, config)` 将应用身份、配置和目标组合在一起。创建 wrapper 会保存这些数据；实际注册发生在挂载时，由 core 验证 manifest 和权限。如果 core 尚未启动，wrapper 会先用该配置初始化，再挂载。

```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 });
```

URL 必须提供 `widget` 的 manifest。`mount(selector)` 可选择其他目标，并保存它，供后续无参数调用使用。挂载、卸载、可见性、remount 和 reload 方法都解析为同一个 wrapper，方便结束后检查状态。

| 成员 | 用途 |
| --- | --- |
| `mount(selector?)` / `start(selector?)` | 必要时注册，并挂载到字符串选择器指定的位置 |
| `unmount(options?)` / `stop()` | 卸载；stop 使用默认选项 |
| `hide()` / `show()` | 保留并重新激活已挂载应用 |
| `remount(selector?)` | 强制卸载后重新挂载 |
| `reload()` | 卸载，清除可用的 loader/manifest 缓存，然后重新挂载 |
| `isMounted` / `isHidden` | 查询 wrapper 和 core 状态 |
| `info` | 名称、URL、选择器、挂载情况和 status |
| `verify()` | 检查注册、选择器和容器 Shadow DOM |

如果另一个 `init` 正在进行，wrapper 会等待它完成，之后在应用仍未注册时注册。同一个 **wrapper** 上的两个并发 mounts 共享注册 Promise；每次挂载仍参与 core 的所有权与引用计数。失败会释放该 Promise，以便重试。保留宿主已有的注册，不会用另一个 wrapper 的 grants 覆盖它。

`reload()` 不会使浏览器原生 ESM 模块缓存失效，也不保证同一 URL 会下载不同版本。更新已部署版本时，应发布带构建身份的 entries，并由 shell 协调加载。

`verify().container.hasContent` 检查 slot 的 ShadowRoot 子节点；false 不能证明拥有自身 DOM 所有权或使用 light DOM 的 renderer 为空。`WuApp.destroy()` 从公开映射中删除应用，但不会清理所有关联的 manifest 注册。移除 renderer 使用 `unmount`；重新初始化整个 shell 使用 `wu.destroy()` 后再次 `init`，不要将 wrapper 的 destroy 当作通用的身份替换操作。

<span id="referencia-de-implementación"></span>

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

## 实现参考

Slots：`src/index.d.ts`；定义：`src/core/wu-core.js`；挂载：`src/core/wu-core.js`；更新：`src/core/wu-core.js`；拆卸：`src/core/wu-core.js`；keep-alive：`src/core/wu-core.js`；wrapper：`src/core/wu-app.js`。测试：`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`。