# 远程应用与能力端口

通过具有授权与撤销机制的 iframe 和 MessagePort 连接其他源上的应用。

本页介绍 WU Framework `4.0.0`。升级现有项目之前，请阅读 [Framework 4.0 与 CLI 1.2 迁移指南](/zh-cn/docs/guides/releases/)。

<span id="configurar-el-host"></span>

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

## 配置宿主

```js
import { wu } from 'wu-framework/core';
import 'wu-framework/ports';
await wu.init({ apps: [{
  name: 'widget', url: 'https://widgets.example/app/', sandbox: 'remote',
  manifest: { entry: 'index.html', wu: { permissions: ['events.emit:widget:*'] } },
  grants: ['events.emit:widget:*'],
  iframe: { title: 'Remote widget', timeout: 15000, callTimeout: 10000 },
}] });
await wu.mount('widget', '#widget-slot');
await wu.update('widget', { theme: 'light' });
```

在宿主创建 `#widget-slot`，并从配置的源提供应用。WU 创建 iframe，并向 URL 的精确源传递端口。浏览器执行跨源隔离；端口消息通过 `widget` 门面执行，并受请求及授予权限的约束。

<span id="conectar-el-documento-remoto"></span>

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

## 连接远程文档

```js
import { connect } from 'wu-framework/remote';

const remote = await connect({ hostOrigin: 'https://shell.example' });
const off = remote.onProps(props => {
  document.documentElement.dataset.theme = props.theme;
});
await remote.emit('widget:ready', { ready: true });
await remote.resize(document.body.scrollHeight);

window.addEventListener('pagehide', () => {
  off();
  remote.close();
}, { once: true });
```

远程文档使用自己的渲染器，并应在 `hostOrigin` 中只接受宿主实际的源。对宿主的调用是异步的：应等待 `store.get`、`store.set`、`emit` 与 RPC。`onProps` 接收更新，`props` 保留最新值；`resize` 请求高度，`whenClosed` 用于观察关闭。数据必须支持 structured clone。

iframe 默认使用 `allow-scripts allow-same-origin allow-forms`，可通过 `iframe.sandbox` 和 `iframe.allow` 配置。`wu.remoteOf(name)` 返回其窗口。`hide` 保留 iframe；`unmount` 移除 iframe 并撤销端口。连接错误为 `WU_REMOTE_TIMEOUT` 和 `WU_REMOTE_LOAD_FAILED`；远程处理器超时返回 `WU_PORT_CALL_TIMEOUT`。

<span id="cabeceras-de-ambos-orígenes"></span>

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

## 两个源的响应头

```json
{ "security": { "frameOrigins": ["https://widgets.example"] } }
```

在 WU CLI `1.0.0` 中，`security.frameOrigins` 将源加入 `frame-src`，不会加入 `script-src` 或 `connect-src`。远程响应必须通过 `frame-ancestors` 允许宿主，且不能被 `X-Frame-Options` 阻止。远程构建还需配置渲染器与 `wu-framework/remote` 入口。

<span id="crear-un-puerto-manualmente"></span>

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

## 手动创建端口

```js
const capability = await wu.ports.mint('widget', { props: { theme: 'dark' } });
worker.postMessage({ wu: 1, type: 'port', app: 'widget' }, [capability.port]);
await capability.ready;
capability.push({ theme: 'light' });
capability.close('finished');
const active = wu.ports.list();
const revoked = wu.ports.revoke('widget');
```

宿主需创建 `worker`，并预先注册带授权的 `widget`；接收端必须连接远程协议。端口可转移、可关闭。`close` 撤销当前端口，`revoke` 关闭该应用的端口。待处理调用返回 `WU_PORT_CLOSED`。消息通信不会在文档间直接共享 store 对象。

<span id="validación-de-origen-en-40"></span>

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

## 4.0 中的 origin 验证

`connect(options)` 和 `waitForPort(options)` 要求 `hostOrigin`；缺少时产生 `WU_REMOTE_HOST_ORIGIN_REQUIRED`。`"*"` 表示明确接受任意 origin。在 iframe 或 popup 中还会验证发送者是否为 `parent` 或 `opener`；`source` 可指定其他窗口。`connect(port)` 重载接受已收到的 port。

应用 origin 取自其 `url`。其他 origin 上的 `entry` 会产生 `WU_REMOTE_ORIGIN_MISMATCH`。与 shell 同源的远程应用会产生 `WU_REMOTE_SAME_ORIGIN`，除非设置 `iframe.allowSameOrigin: true`；该例外将失去跨 origin 隔离。`wu.detach()` 以 `WU_WINDOW_REMOTE_UNSUPPORTED` 拒绝远程应用。

host 会在 `load` 后持续提供 port，直到收到响应，因此远程端可先等待其他工作再连接。通过 port 使用 `consume()` 时，仅允许已声明 `shape` 内的方法；没有 `shape` 时排除 `Object.prototype` 方法。卸载和 `destroy()` 会撤销 core 的 ports。