# 保留状态的编辑器

通过两个真实微应用体验窗口与 PiP、拖放、过渡和 keep-alive。

打开[移动中的应用](/zh-cn/labs/interaction/)。React 保留编辑器草稿，Vue 管理收到笔记的收件箱。WU 在同一文档中使用独立的 Shadow DOM 根挂载两个应用，并协调它们的交互。示例使用构建中包含的 WU Framework `4.0.0`。

<span id="cuatro-tareas-un-escenario"></span>

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

## 四项任务，一个场景

| 操作 | 观察内容 |
| --- | --- |
| 输入并发送笔记 | 将卡片拖放到 Vue 或使用发送按钮。完成类型和权限检查后，收件箱会收到数据副本。 |
| 隐藏并显示编辑器 | 草稿和 React 实例保持不变，不会重新执行挂载。 |
| 改变面板布局 | 过渡动画改变布局，输入和接收笔记仍可正常使用。 |
| 移出并返回编辑器 | 打开窗口，在其中输入，然后返回。同一组节点会移回宿主。浏览器提供该 API 时才启用 PiP。 |

<span id="qué-pertenece-a-cada-app"></span>

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

## 每个应用负责什么

Shell 拥有一个 `new WuCore()` 实例，注册两个应用并授予权限。React 负责草稿并提供 `docs.note` 类型；Vue 验证并接收该类型。可视控件是 Lit Custom Elements。`context.wu` 上下文在发送或接收数据时保留各微应用的权限身份。

以下代码片段在初始化运行时并挂载应用后执行。`runtime` 是 Shell 实例。要复现完整启动过程，请查看本指南末尾的源代码。

<span id="ventanas-y-picture-in-picture"></span>

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

## 窗口与画中画

```js
// Prepare the optional subsystem before the user clicks.
await runtime.windowsReady();

openButton.addEventListener('click', async () => {
  try {
    await runtime.detach('interaction-editor', {
      window: 'popup', width: 560, height: 520,
    });
    status.textContent = String(runtime.isDetached('interaction-editor'));
  } catch (error) {
    status.textContent = error.message;
  }
});

returnButton.addEventListener('click', () => {
  runtime.attach('interaction-editor');
});
```

请先创建 `openButton`、`returnButton` 和 `status`。对于 PiP，请检查支持情况后，在点击处理中使用 `window: "pip"`。`windowOf(name)` 可查询当前窗口。关闭窗口会将应用移回页面；离开此场景则会卸载应用。模块继续在原始页面中执行，编辑器保留相同的节点和状态。

<span id="entrega-tipada-con-ratón-o-teclado"></span>

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

## 通过鼠标或键盘进行类型化传递

```js
// Sending app: its manifest requests drag.offer:docs.note.
const stopOffer = await context.wu.drag.offer(card, {
  type: 'docs.note',
  payload: () => ({ text: readDraft() }),
});

// The send button uses the same permission and validation path.
await context.wu.drag.deliver('docs.note', { text: readDraft() }, {
  to: 'interaction-board',
});

// Receiving app: its manifest requests drag.accept:docs.note.
const stopAccept = await context.wu.drop.accept(inbox, {
  type: 'docs.note',
  validate(note) {
    if (!note || typeof note.text !== 'string' || !note.text.trim()) {
      throw new TypeError('A non-empty note is required');
    }
    return note;
  },
  onDrop: note => addToInbox(note),
});
```

该片段展示了两端逻辑：提供和发送属于编辑器，接收属于收件箱。请将 `card`、`inbox`、`readDraft` 和 `addToInbox` 替换为各应用中的引用。宿主必须向对应应用授予两项权限。保留 `stopOffer` 和 `stopAccept`，并在卸载时调用。

<span id="conservar-estado-y-cambiar-la-distribución"></span>

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

## 保留状态并改变布局

```js
await runtime.unmount('interaction-editor', { keepAlive: true });
await runtime.show('interaction-editor');

// panelA and panelB have stable data-wu-vt keys inside workspace.
const animation = runtime.transition(() => {
  workspace.classList.toggle('stacked');
}, { root: workspace, duration: 320 });
await animation.updateCallbackDone;
await animation.finished;
```

定义 `stacked` 类来将面板排列为一列，并向 WU 需要测量的元素添加 `data-wu-vt`。隐藏会保留实例，但不会自动停止你创建的定时器或连接。过渡会遵循减少动态效果设置。要完全移除此场景，请调用 `runtime.destroy()` 并释放宿主的外部资源。

<span id="fuentes-y-referencias"></span>

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

## 源码与参考

[官方仓库中的完整示例](https://git.wu-framework.com/Padre/wu-website/src/branch/main/apps/interaction-lab)包含清单和构建说明。[Lit 控件](https://git.wu-framework.com/Padre/wu-website/src/branch/main/packages/interaction-lab-ui)属于同一项目。请克隆完整仓库并按照 README 运行；下方的三个下载文件展示了集成代码和控制器。

阅读 [Shell 控制器](/microfrontends/interaction-lab/source/controller.js.txt)、[React 编辑器](/microfrontends/interaction-lab/source/react-editor.js.txt)和 [Vue 收件箱](/microfrontends/interaction-lab/source/vue-board.js.txt)。这些是用于构建示例的源文件。有关签名、权限和错误，请查看[拖放](/zh-cn/docs/framework/drag-and-drop/)、[窗口与焦点](/zh-cn/docs/framework/ui-coordination/)以及[生命周期](/zh-cn/docs/framework/lifecycle/)。

PiP 支持取决于浏览器；[Chrome 文档](https://developer.chrome.com/docs/web-platform/document-picture-in-picture)介绍了用户手势和窗口生命周期。发送按钮允许读者无需拖动即可完成任务，符合 [WCAG 拖动操作准则](https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html)。