WU / 文档通用微前端
中文

保留状态的编辑器

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

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

四项任务,一个场景

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

每个应用负责什么

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

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

窗口与画中画

// 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) 可查询当前窗口。关闭窗口会将应用移回页面;离开此场景则会卸载应用。模块继续在原始页面中执行,编辑器保留相同的节点和状态。

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

// 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,并在卸载时调用。

保留状态并改变布局

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() 并释放宿主的外部资源。

源码与参考

官方仓库中的完整示例包含清单和构建说明。Lit 控件属于同一项目。请克隆完整仓库并按照 README 运行;下方的三个下载文件展示了集成代码和控制器。

阅读 Shell 控制器、React 编辑器和 Vue 收件箱。这些是用于构建示例的源文件。有关签名、权限和错误,请查看拖放、窗口与焦点以及生命周期。

PiP 支持取决于浏览器;Chrome 文档介绍了用户手势和窗口生命周期。发送按钮允许读者无需拖动即可完成任务,符合 WCAG 拖动操作准则。

一起完善 WU

发现了问题?有新的想法?

你的使用体验能让 WU 变得更好。告诉我们遇到的问题,或支持项目的持续开发。