WU / 文档通用微前端
中文

共享状态

独立读取副本、写时复制、按路径订阅、batch 与副本间同步。

同一数据,多个 renderers

store 让 React、Vue 和 shell 展示同一状态。数据通过点分路径寻址。共享数据应描述领域或交互;DOM 节点、React roots 和 Vue 实例属于各应用的生命周期。

要从真实 workspace 实现,参阅 React 与 Vue 共享计数器。其中包含完整组件、初始读取、订阅、清理,以及 manifest 和 shell 两侧的权限配置。

以下片段放在 shell 客户端模块中,其中 wu 从 wu-framework 导入。在微应用生命周期内,使用 context.wu 并为所需路径请求 store.read 和 store.write 权限;shell 必须授予它们。配置与权限展示两侧配置。要观察共享写入,打开 React 与 Vue并点击 从 React +1。

wu.store.set('catalog.selection', { id: 'p-42', quantity: 1 });

const stop = wu.store.on('catalog.selection', (selection) => {
  console.log(selection.id, selection.quantity);
});

wu.store.update('catalog.selection', (selection) => ({
  ...selection,
  quantity: selection.quantity + 1,
}));

await Promise.resolve(); // Permite entregar las notificaciones de la microtask.
stop();

set 和 update 返回序号。update 向 updater 提供独立快照,要求同步结果;返回 Promise 会被拒绝。读取异步数据时,在外部执行 fetch,随后写入结果。

副本、身份与响应性

get(path) 和 getSnapshot(path) 返回与内部状态分离的数据。修改收到的对象不会改变 store。写入采用写时复制,内部状态被冻结,因此对子路径的写入不会反向修改之前的历史记录。

const selection = wu.store.get('catalog.selection');
selection.quantity = 99;
console.log(wu.store.get('catalog.selection').quantity); // Sigue en el store

每次读取对象都会生成新快照。不要在没有维持通知间身份稳定的情况下,将 store.get() 直接作为 React useSyncExternalStore 的 getSnapshot。 手动桥接时,保存本地快照,仅在收到通知时替换;也可使用框架状态,通过 on 更新。

通知通过 microtasks 调度。set 之后立即读取会看到新状态,但回调不一定已经执行。订阅不会自动交付初始值:连接时先读取一次。

精确订阅与通配符

订阅 观察什么 回调
on('catalog', fn) catalog 及其后代路径的写入 (valorDeCatalog, 'catalog')
on('catalog.selection', fn) 该路径及其后代 (valorDelPath, pathSuscrito)
on('catalog.*', fn) catalog 下的一个直接路径段 ({ path, value })
on('*', fn) 所有写入 ({ path, value })

catalog.* 不匹配 catalog 或 catalog.selection.quantity:通配符替代一个路径段,而非整棵子树。替换父路径时,不会自动向所有子路径精确订阅发送仿佛分别写入的通知。对于展示完整对象的组件,应订阅对象路径并重新读取它。

按订阅方式选择 payload,即使通用 TypeScript 签名没有区分这两种情况:

const stopValue = wu.store.on('catalog.selection', (selection) => {
  console.log('Selección actual', selection);
});
const stopChanges = wu.store.on('catalog.*', ({ path, value }) => {
  console.log('Campo escrito', path, value);
});

wu.store.set('catalog.selection', { id: 'p-42', quantity: 2 });
await Promise.resolve();
// Al retirar el consumidor, libera ambas suscripciones.
stopValue();
stopChanges();

支持的值

本地 codec 支持 null、布尔值、字符串、有限数字、标量 bigint、稠密数组和普通数据对象。拒绝函数、符号、作为写入值的 undefined、循环引用、访问器、稀疏数组、Date/Map 等实例,以及超过 256 层或 100 000 个节点的结构。

读取不存在的路径可以返回 undefined。共享日期使用 ISO 字符串;需要通过 JSON 传输的大整数 ID 使用字符串。本地 bigint 支持不代表与 sync 兼容,后者要求消息数据对 JSON 安全。

路径拒绝空段,也禁止写入 __proto__、prototype 或 constructor。危险读取返回 undefined,不会暴露内部对象。

Batch 与原子性边界

wu.store.batch({
  'catalog.selection': { id: 'p-42', quantity: 2 },
  'checkout.step': 'review',
});

batch 在执行任何写入前验证并分离全部路径和值。如果预检失败,状态、序号和指标均不改变。验证后,每个条目仍有独立写入和序号;底层 taps 观察到单独的 commits。它不是数据库事务,也不是外部副作用事务。

按需同步

const sync = wu.store.sync({ transport: 'broadcast', room: 'docs-demo' });
await sync.ready();

// Escribir después de conectar registra estos paths para otros participantes.
wu.store.set('demo.counter', 1);
console.log(sync.status());

// Al abandonar la sesión:
sync.stop();

模块延迟加载。BroadcastChannel 让兼容的浏览器上下文通信;WebSocket 和自定义传输扩展传输方式。冲突解决采用按路径的 Last Writer Wins,按 Lamport 时钟和站点标识排序。

只跟踪启用 sync 后的写入。父/子路径共享 Lamport 顺序:较新的父节点覆盖旧子节点,较新的子节点在父节点延迟到达时仍保留。加入时的 snapshots 保持该顺序。不同步根替换,也不自动提供持久化。

内置 WebSocket 传输要求同步的 authorizePeer(message),除非宿主因中继已经验证参与者身份而声明 trustTransport: true。共享房间名称不构成数据授权。

实现参考

Codec:src/core/wu-store.js;读取/写入:src/core/wu-store.js;更新:src/core/wu-store.js;batch:src/core/wu-store.js;通知:src/core/wu-store.js;sync:src/core/wu-store-sync.js。测试:tests/unit/store.test.js、tests/unit/store-snapshot-identity.test.js、tests/unit/store-subscription-semantics.test.js、tests/unit/store-sync.test.js。

副本范围

paths 通过路径片段前缀限制输入和输出操作:cart 包括该路径及其后代。authorizeOp(op, message) 必须同步返回 true 才接受传入操作。远程写入直接进入 store,不由应用 grants 授权。runtime 还限制 Lamport 时钟跳变。

const sync = wu.store.sync({
  transport: 'broadcast', room: 'cart-demo', paths: ['cart'],
  authorizeOp: op => op.path === 'cart.count' && Number.isSafeInteger(op.value),
});
await sync.ready();
wu.store.set('cart.count', 1);
// Teardown:
sync.stop();
一起完善 WU

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

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