WU / 文档通用微前端
中文

事件、RPC 与契约

三种具体通信机制:通知事实、请求响应以及使用有版本的服务。

按交互类型选择

需求 API 结果
通知事实,无需响应 emit / on 带身份和数据的事件
向提供方请求响应 request / handle 返回值或错误的 Promise
调用本地服务的方法 provide / consume 带版本契约的代理

这三种机制都位于浏览器的 JavaScript 进程中。此处的 RPC 不意味着 HTTP 或服务器间传输。实现内部可以调用 fetch,但该副作用及其授权由实现负责。

使用 wu 的片段假定已在 shell 客户端模块中执行 import { wu } from 'wu-framework',并通过 await wu.init(...) 初始化。使用 appWu 的片段采用在 mount(container, context) 中交给应用的 context.wu。需要可复制的完整示例,请参阅应用间求和:包括客户端、提供方、manifest、grants 和 Astro 页面,并展示可观察的响应以及可恢复的错误。

事件与 envelope

// Código del shell, con autoridad host.
const stop = wu.on('catalog:*', (event) => {
  console.log(event.name, event.data, event.appName, event.verified);
});

const accepted = wu.emit('catalog:selected', { productId: 'p-42' });
if (!accepted) console.info('El bus rechazó el evento');
stop();

回调接收包含 name、data、timestamp、appName、meta 和 verified 的对象。数据跨越总线边界时会被分离并冻结。once 在首次交付后移除订阅;off 移除指定订阅。

在应用中使用 context.wu.on 和 context.wu.emit。facade 会附加应用身份并验证 grants。严格模式下,身份无效、缺少能力或向保留命名空间发射时返回 false。被拒绝的订阅和 RPC 可能抛出错误:检查 emit 的布尔返回值,并处理会抛出异常的 API。

总线默认最多保存 100 个事件。eventBus.replay(pattern, callback) 将已有记录再次交给指定回调;它不会重新执行整个系统,也不保证恢复外部副作用。history: false 可将一次发射排除在历史记录之外。

具有超时和版本的 RPC

const release = wu.eventBus.handle('catalog:lookup', ({ productId }) => {
  return { productId, title: 'Producto de demostración' };
}, { version: '1.0.0', allow: ['search'] });

// Dentro del lifecycle de la app search, con rpc.call:catalog:lookup:
async function lookup(appWu, productId, signal) {
  return appWu.request('catalog:lookup', { productId }, {
    version: '^1.0.0',
    timeout: 2500,
    signal,
  });
}

// Cuando el proveedor del shell ya no deba atender:
release();

提供方应用需要针对通道的 rpc.provide,使用方需要 rpc.call。此外,allow 会过滤调用者身份。严格模式下,具有应用作用域的提供方默认 allow 仅允许自身应用;应用间通信需要明确声明使用方。

wu.handle(channel, handler, options) 保留 version、allow 和 replace。在生命周期中,context.wu.handle 还加入应用身份和 grants;微应用 handlers 应使用该 facade。

request 的默认超时为 0,即不限时。UI 交互应设置有限超时。handler 的第二个参数包含关联信息、身份和一个 signal。handler 应配合该 signal 取消自身工作;拒绝等待不会撤销已经执行的操作。

一个通道只有一个活动提供方。在严格条件下替换同一提供方需要 { replace: true };其他身份不能占用已有所有者的通道。释放、替换和撤销提供方都会中止关联请求。总线会发射带有关联信息的 <canal>:request、<canal>:response 和 <canal>:error 跟踪事件。

本地方法契约

const handle = wu.provide('formatters', {
  price(value) {
    return new Intl.NumberFormat('es-MX', {
      style: 'currency', currency: 'MXN',
    }).format(value);
  },
}, {
  version: '1.0.0',
  shape: { price: 'function' },
});

const formatters = wu.consume('formatters', '^1.0.0');
console.log(formatters.price(120));
handle.revoke();

consume 返回动态代理。不使用 { wait: true } 时,创建代理不要求提供方已经存在:首次访问成员才会验证存在性和版本。使用 { wait: true, timeout: 2500 } 时,会等待兼容的提供方出现。

provide 的默认版本为 0.0.0,而 RPC handle 的默认版本为 1.0.0。shape 验证成员存在性或 typeof,不能替代完整的参数与结果 schema。

应用 facade 对契约同样要求 rpc.provide/rpc.call,并自动将提供方关联到应用。使用方得到的代理不是 Promise;无法通过该代理访问名为 then 的方法。契约没有实现与总线 RPC 相同的允许列表和载荷分离机制;选择契约时应考虑这一限制。

应当处理的错误

RPC 错误码 原因
WU_RPC_NO_HANDLER 通道没有提供方
WU_RPC_VERSION_MISMATCH 版本不满足范围
WU_RPC_CALLER_DENIED 使用方不在 allow 中
WU_RPC_CALL_DENIED / WU_RPC_PROVIDE_DENIED 缺少有效 grant
WU_RPC_CHANNEL_OWNED 其他所有者已注册此通道
WU_RPC_REPLACE_REQUIRED 未明确声明替换意图
WU_RPC_PROVIDER_RELEASED / WU_RPC_PROVIDER_REPLACED 请求过程中提供方发生变化

实现参考

事件:src/core/wu-event-bus.js;RPC:src/core/wu-event-bus.js 和 src/core/wu-event-bus.js;单例 handle 快捷方法:src/index.js;replay:src/core/wu-event-bus.js;契约:src/core/wu-contracts.js 和 src/core/wu-contracts.js;facade:src/core/wu-core.js。测试:tests/unit/event-bus.test.js、tests/unit/contracts.test.js、tests/unit/capability-security-v3.test.js、tests/unit/scoped-events-teardown.test.js。

首个 provider 拥有合同名称。在所有者撤销合同或卸载之前,其他应用或尝试替换应用合同的 host 会收到 WU_CAPABILITY_OWNED。同一所有者可替换其实现。当存在 shape 时,远程 ports 仅暴露其中声明的方法。

一起完善 WU

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

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