事件、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 仅暴露其中声明的方法。