request-iframe 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/QUICKSTART.CN.md +4 -2
- package/QUICKSTART.md +4 -2
- package/README.CN.md +302 -54
- package/README.md +281 -36
- package/cdn/request-iframe-react.umd.js +3354 -0
- package/cdn/request-iframe-react.umd.js.map +1 -0
- package/cdn/request-iframe-react.umd.min.js +2 -0
- package/cdn/request-iframe-react.umd.min.js.map +1 -0
- package/cdn/request-iframe.umd.js +19735 -0
- package/cdn/request-iframe.umd.js.map +1 -0
- package/cdn/request-iframe.umd.min.js +4 -0
- package/cdn/request-iframe.umd.min.js.map +1 -0
- package/esm/api/client.js +31 -22
- package/esm/api/endpoint.js +229 -0
- package/esm/api/server.js +19 -9
- package/esm/constants/debug.js +17 -0
- package/esm/constants/index.js +115 -66
- package/esm/constants/log.js +11 -0
- package/esm/constants/messages.js +6 -1
- package/esm/constants/warn-once.js +15 -0
- package/esm/endpoint/facade.js +390 -0
- package/esm/endpoint/heartbeat/heartbeat.js +60 -0
- package/esm/endpoint/heartbeat/ping.js +20 -0
- package/esm/endpoint/index.js +13 -0
- package/esm/endpoint/infra/hub.js +316 -0
- package/esm/endpoint/infra/inbox.js +232 -0
- package/esm/endpoint/infra/outbox.js +408 -0
- package/esm/endpoint/stream/dispatcher.js +58 -0
- package/esm/endpoint/stream/errors.js +27 -0
- package/esm/endpoint/stream/factory.js +76 -0
- package/esm/endpoint/stream/file-auto-resolve.js +34 -0
- package/esm/endpoint/stream/file-writable.js +105 -0
- package/esm/endpoint/stream/handler.js +26 -0
- package/esm/{core → impl}/client.js +243 -320
- package/esm/{core → impl}/response.js +120 -154
- package/esm/impl/server.js +568 -0
- package/esm/index.js +13 -6
- package/esm/message/ack.js +27 -0
- package/esm/message/channel-cache.js +108 -0
- package/esm/message/channel.js +92 -5
- package/esm/message/dispatcher.js +149 -98
- package/esm/stream/error.js +22 -0
- package/esm/stream/index.js +3 -1
- package/esm/stream/readable-stream.js +101 -26
- package/esm/stream/stream-core.js +121 -3
- package/esm/stream/writable-stream.js +368 -43
- package/esm/utils/ack.js +36 -0
- package/esm/utils/blob.js +16 -0
- package/esm/utils/cache.js +25 -76
- package/esm/utils/content-type.js +81 -0
- package/esm/utils/debug.js +157 -180
- package/esm/utils/hooks.js +130 -0
- package/esm/utils/id.js +14 -0
- package/esm/utils/iframe.js +20 -0
- package/esm/utils/index.js +12 -162
- package/esm/utils/is.js +3 -0
- package/esm/utils/logger.js +55 -0
- package/esm/utils/origin.js +3 -1
- package/esm/utils/promise.js +3 -0
- package/esm/utils/window.js +31 -0
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +32 -23
- package/library/api/endpoint.d.ts +23 -0
- package/library/api/endpoint.d.ts.map +1 -0
- package/library/api/endpoint.js +235 -0
- package/library/api/server.d.ts +4 -1
- package/library/api/server.d.ts.map +1 -1
- package/library/api/server.js +19 -9
- package/library/constants/debug.d.ts +18 -0
- package/library/constants/debug.d.ts.map +1 -0
- package/library/constants/debug.js +23 -0
- package/library/constants/index.d.ts +58 -7
- package/library/constants/index.d.ts.map +1 -1
- package/library/constants/index.js +143 -67
- package/library/constants/log.d.ts +12 -0
- package/library/constants/log.d.ts.map +1 -0
- package/library/constants/log.js +17 -0
- package/library/constants/messages.d.ts +6 -1
- package/library/constants/messages.d.ts.map +1 -1
- package/library/constants/messages.js +6 -1
- package/library/constants/warn-once.d.ts +12 -0
- package/library/constants/warn-once.d.ts.map +1 -0
- package/library/constants/warn-once.js +22 -0
- package/library/endpoint/facade.d.ts +238 -0
- package/library/endpoint/facade.d.ts.map +1 -0
- package/library/endpoint/facade.js +398 -0
- package/library/endpoint/heartbeat/heartbeat.d.ts +34 -0
- package/library/endpoint/heartbeat/heartbeat.d.ts.map +1 -0
- package/library/endpoint/heartbeat/heartbeat.js +67 -0
- package/library/endpoint/heartbeat/ping.d.ts +18 -0
- package/library/endpoint/heartbeat/ping.d.ts.map +1 -0
- package/library/endpoint/heartbeat/ping.js +26 -0
- package/library/endpoint/index.d.ts +16 -0
- package/library/endpoint/index.d.ts.map +1 -0
- package/library/endpoint/index.js +114 -0
- package/library/endpoint/infra/hub.d.ts +170 -0
- package/library/endpoint/infra/hub.d.ts.map +1 -0
- package/library/endpoint/infra/hub.js +323 -0
- package/library/endpoint/infra/inbox.d.ts +73 -0
- package/library/endpoint/infra/inbox.d.ts.map +1 -0
- package/library/endpoint/infra/inbox.js +239 -0
- package/library/endpoint/infra/outbox.d.ts +149 -0
- package/library/endpoint/infra/outbox.d.ts.map +1 -0
- package/library/endpoint/infra/outbox.js +415 -0
- package/library/endpoint/stream/dispatcher.d.ts +33 -0
- package/library/endpoint/stream/dispatcher.d.ts.map +1 -0
- package/library/endpoint/stream/dispatcher.js +66 -0
- package/library/endpoint/stream/errors.d.ts +20 -0
- package/library/endpoint/stream/errors.d.ts.map +1 -0
- package/library/endpoint/stream/errors.js +32 -0
- package/library/endpoint/stream/factory.d.ts +44 -0
- package/library/endpoint/stream/factory.d.ts.map +1 -0
- package/library/endpoint/stream/factory.js +82 -0
- package/library/endpoint/stream/file-auto-resolve.d.ts +26 -0
- package/library/endpoint/stream/file-auto-resolve.d.ts.map +1 -0
- package/library/endpoint/stream/file-auto-resolve.js +41 -0
- package/library/endpoint/stream/file-writable.d.ts +33 -0
- package/library/endpoint/stream/file-writable.d.ts.map +1 -0
- package/library/endpoint/stream/file-writable.js +115 -0
- package/library/endpoint/stream/handler.d.ts +20 -0
- package/library/endpoint/stream/handler.d.ts.map +1 -0
- package/library/endpoint/stream/handler.js +32 -0
- package/library/{core → impl}/client.d.ts +16 -13
- package/library/impl/client.d.ts.map +1 -0
- package/library/{core → impl}/client.js +254 -333
- package/library/{core → impl}/request.d.ts.map +1 -1
- package/library/{core → impl}/response.d.ts +7 -12
- package/library/impl/response.d.ts.map +1 -0
- package/library/{core → impl}/response.js +120 -154
- package/library/{core → impl}/server.d.ts +26 -55
- package/library/impl/server.d.ts.map +1 -0
- package/library/impl/server.js +575 -0
- package/library/index.d.ts +13 -6
- package/library/index.d.ts.map +1 -1
- package/library/index.js +16 -16
- package/library/message/ack.d.ts +15 -0
- package/library/message/ack.d.ts.map +1 -0
- package/library/message/ack.js +33 -0
- package/library/message/channel-cache.d.ts +26 -0
- package/library/message/channel-cache.d.ts.map +1 -0
- package/library/message/channel-cache.js +115 -0
- package/library/message/channel.d.ts +53 -6
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/channel.js +96 -9
- package/library/message/dispatcher.d.ts +17 -0
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +149 -98
- package/library/stream/error.d.ts +24 -0
- package/library/stream/error.d.ts.map +1 -0
- package/library/stream/error.js +29 -0
- package/library/stream/index.d.ts +4 -1
- package/library/stream/index.d.ts.map +1 -1
- package/library/stream/index.js +7 -4
- package/library/stream/readable-stream.d.ts.map +1 -1
- package/library/stream/readable-stream.js +102 -27
- package/library/stream/stream-core.d.ts +22 -1
- package/library/stream/stream-core.d.ts.map +1 -1
- package/library/stream/stream-core.js +120 -2
- package/library/stream/types.d.ts +115 -2
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.ts +20 -2
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +366 -41
- package/library/types/index.d.ts +17 -22
- package/library/types/index.d.ts.map +1 -1
- package/library/utils/ack.d.ts +2 -0
- package/library/utils/ack.d.ts.map +1 -0
- package/library/utils/ack.js +44 -0
- package/library/utils/blob.d.ts +3 -0
- package/library/utils/blob.d.ts.map +1 -0
- package/library/utils/blob.js +22 -0
- package/library/utils/cache.d.ts +10 -20
- package/library/utils/cache.d.ts.map +1 -1
- package/library/utils/cache.js +25 -79
- package/library/utils/content-type.d.ts +13 -0
- package/library/utils/content-type.d.ts.map +1 -0
- package/library/utils/content-type.js +87 -0
- package/library/utils/debug.d.ts.map +1 -1
- package/library/utils/debug.js +156 -178
- package/library/utils/hooks.d.ts +30 -0
- package/library/utils/hooks.d.ts.map +1 -0
- package/library/utils/hooks.js +139 -0
- package/library/utils/id.d.ts +9 -0
- package/library/utils/id.d.ts.map +1 -0
- package/library/utils/id.js +21 -0
- package/library/utils/iframe.d.ts +5 -0
- package/library/utils/iframe.d.ts.map +1 -0
- package/library/utils/iframe.js +25 -0
- package/library/utils/index.d.ts +7 -34
- package/library/utils/index.d.ts.map +1 -1
- package/library/utils/index.js +58 -193
- package/library/utils/is.d.ts +2 -0
- package/library/utils/is.d.ts.map +1 -0
- package/library/utils/is.js +9 -0
- package/library/utils/logger.d.ts +13 -0
- package/library/utils/logger.d.ts.map +1 -0
- package/library/utils/logger.js +63 -0
- package/library/utils/origin.d.ts.map +1 -1
- package/library/utils/origin.js +2 -1
- package/library/utils/promise.d.ts +2 -0
- package/library/utils/promise.d.ts.map +1 -0
- package/library/utils/promise.js +9 -0
- package/library/utils/window.d.ts +2 -0
- package/library/utils/window.d.ts.map +1 -0
- package/library/utils/window.js +38 -0
- package/package.json +49 -2
- package/react/package.json +2 -1
- package/esm/core/client-server.js +0 -329
- package/esm/core/server.js +0 -767
- package/esm/utils/ack-meta.js +0 -53
- package/library/core/client-server.d.ts +0 -106
- package/library/core/client-server.d.ts.map +0 -1
- package/library/core/client-server.js +0 -336
- package/library/core/client.d.ts.map +0 -1
- package/library/core/response.d.ts.map +0 -1
- package/library/core/server.d.ts.map +0 -1
- package/library/core/server.js +0 -772
- package/library/utils/ack-meta.d.ts +0 -2
- package/library/utils/ack-meta.d.ts.map +0 -1
- package/library/utils/ack-meta.js +0 -59
- /package/esm/{core → impl}/request.js +0 -0
- /package/library/{core → impl}/request.d.ts +0 -0
- /package/library/{core → impl}/request.js +0 -0
package/QUICKSTART.CN.md
CHANGED
|
@@ -220,9 +220,11 @@ server.on('/api/users/:id', (req, res) => {
|
|
|
220
220
|
开启 trace 模式查看详细日志:
|
|
221
221
|
|
|
222
222
|
```typescript
|
|
223
|
+
import { LogLevel } from 'request-iframe';
|
|
224
|
+
|
|
223
225
|
const client = requestIframeClient(iframe, {
|
|
224
226
|
secretKey: 'my-app',
|
|
225
|
-
trace:
|
|
227
|
+
trace: LogLevel.INFO // 输出 info/warn/error(也可以用 true 开启 TRACE)
|
|
226
228
|
});
|
|
227
229
|
|
|
228
230
|
const server = requestIframeServer({
|
|
@@ -293,4 +295,4 @@ console.log(response.data.name); // TypeScript 知道这是 string
|
|
|
293
295
|
|
|
294
296
|
- 查看 [README.CN.md](./README.CN.md) 了解完整 API(中文)
|
|
295
297
|
- 查看 [README.md](./README.md) 了解完整 API(English)
|
|
296
|
-
- 查看 [
|
|
298
|
+
- 查看 [`__tests__/`](./__tests__) 与 [`react/__tests__/`](./react/__tests__) 下的测试用例获取更多示例
|
package/QUICKSTART.md
CHANGED
|
@@ -220,9 +220,11 @@ server.on('/api/users/:id', (req, res) => {
|
|
|
220
220
|
Enable trace mode to view detailed logs:
|
|
221
221
|
|
|
222
222
|
```typescript
|
|
223
|
+
import { LogLevel } from 'request-iframe';
|
|
224
|
+
|
|
223
225
|
const client = requestIframeClient(iframe, {
|
|
224
226
|
secretKey: 'my-app',
|
|
225
|
-
trace:
|
|
227
|
+
trace: LogLevel.INFO // Enable info/warn/error logs (or use true for TRACE)
|
|
226
228
|
});
|
|
227
229
|
|
|
228
230
|
const server = requestIframeServer({
|
|
@@ -293,4 +295,4 @@ console.log(response.data.name); // TypeScript knows this is string
|
|
|
293
295
|
|
|
294
296
|
- View [README.md](./README.md) for complete API documentation (English)
|
|
295
297
|
- View [README.CN.md](./README.CN.md) for complete API documentation (中文)
|
|
296
|
-
- Check [
|
|
298
|
+
- Check [`__tests__/`](./__tests__) and [`react/__tests__/`](./react/__tests__) for more examples from test cases
|
package/README.CN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# request-iframe
|
|
2
2
|
|
|
3
|
-
像发送 HTTP 请求一样与 iframe 通信!基于 `postMessage`
|
|
3
|
+
像发送 HTTP 请求一样与 iframe / Window 通信!基于 `postMessage` 实现的浏览器跨页面通信库。
|
|
4
4
|
|
|
5
5
|
> 🌐 **Languages**: [English](./README.md) | [中文](./README.CN.md)
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<img src="https://img.shields.io/badge/TypeScript-Ready-blue" alt="TypeScript Ready">
|
|
9
9
|
<img src="https://img.shields.io/badge/API-Express%20Like-green" alt="Express Like API">
|
|
10
10
|
<img src="https://img.shields.io/badge/License-MIT-yellow" alt="MIT License">
|
|
11
|
-
<img src="https://
|
|
11
|
+
<img src="https://coveralls.io/repos/github/gxlmyacc/request-iframe/badge.svg?branch=main" alt="Coverage Status">
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
## 📑 目录
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
## 为什么选择 request-iframe?
|
|
50
50
|
|
|
51
|
-
在微前端、iframe
|
|
51
|
+
在微前端、iframe 嵌套、弹窗(window.open)等场景下,跨页面通信是常见需求。传统的 `postMessage` 通信存在以下痛点:
|
|
52
52
|
|
|
53
53
|
| 痛点 | 传统方式 | request-iframe |
|
|
54
54
|
|------|----------|----------------|
|
|
@@ -74,8 +74,9 @@
|
|
|
74
74
|
- ⏱️ **智能超时** - 三阶段超时(连接/同步/异步),自动识别长任务
|
|
75
75
|
- 📦 **TypeScript** - 完整的类型定义和智能提示
|
|
76
76
|
- 🔒 **消息隔离** - secretKey 机制避免多实例消息串线
|
|
77
|
-
- 📁 **文件传输** - 支持文件通过流方式传输(Client
|
|
77
|
+
- 📁 **文件传输** - 支持文件通过流方式传输(Client↔Server)
|
|
78
78
|
- 🌊 **流式传输** - 支持大文件分块传输,支持异步迭代器
|
|
79
|
+
- 🧾 **分级日志** - 默认只输出 warn/error,可通过 `trace` 设置日志等级与调试日志
|
|
79
80
|
- 🌍 **多语言** - 错误消息可自定义,便于国际化
|
|
80
81
|
- ✅ **协议版本** - 内置版本控制,便于升级兼容
|
|
81
82
|
|
|
@@ -93,6 +94,32 @@ pnpm add request-iframe
|
|
|
93
94
|
|
|
94
95
|
**TypeScript**: 内置完整类型定义,无需安装 `@types/request-iframe`
|
|
95
96
|
|
|
97
|
+
## CDN(UMD bundle)
|
|
98
|
+
|
|
99
|
+
本项目也支持构建 **可直接用 `<script>` 引入的 UMD bundle**(核心 + React hooks),方便放到 CDN 上。
|
|
100
|
+
|
|
101
|
+
- 核心 bundle 输出:`cdn/request-iframe.umd(.min).js` → 全局变量 `RequestIframe`
|
|
102
|
+
- React bundle 输出:`cdn/request-iframe-react.umd(.min).js` → 全局变量 `RequestIframeReact`
|
|
103
|
+
- 依赖 `React` 全局变量(即 `react` 的 UMD 版本)
|
|
104
|
+
- 依赖 `RequestIframe` 全局变量(先加载核心 bundle)
|
|
105
|
+
|
|
106
|
+
示例(使用 unpkg):
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<!-- 核心 -->
|
|
110
|
+
<script src="https://unpkg.com/request-iframe@latest/cdn/request-iframe.umd.min.js"></script>
|
|
111
|
+
|
|
112
|
+
<!-- React(可选) -->
|
|
113
|
+
<script src="https://unpkg.com/react@latest/umd/react.production.min.js"></script>
|
|
114
|
+
<script src="https://unpkg.com/request-iframe@latest/cdn/request-iframe-react.umd.min.js"></script>
|
|
115
|
+
|
|
116
|
+
<script>
|
|
117
|
+
const { requestIframeClient, requestIframeServer, requestIframeEndpoint } = RequestIframe;
|
|
118
|
+
// React hooks 在 RequestIframeReact 上(例如 RequestIframeReact.useClient)
|
|
119
|
+
console.log(!!requestIframeClient, !!requestIframeServer, !!requestIframeEndpoint);
|
|
120
|
+
<\/script>
|
|
121
|
+
```
|
|
122
|
+
|
|
96
123
|
## 快速开始
|
|
97
124
|
|
|
98
125
|
### 1. 父页面(Client 端)
|
|
@@ -130,6 +157,11 @@ server.on('/api/getUserInfo', (req, res) => {
|
|
|
130
157
|
|
|
131
158
|
> 💡 **提示**: 更多快速上手指南请查看 [QUICKSTART.CN.md](./QUICKSTART.CN.md) 或 [QUICKSTART.md](./QUICKSTART.md) (English)
|
|
132
159
|
|
|
160
|
+
## 该用哪个 API?
|
|
161
|
+
|
|
162
|
+
- **优先使用 `requestIframeClient()` + `requestIframeServer()`**:适用于主要是单向通信(父页 → iframe),并且你希望把“发送请求”和“处理请求”职责明确分开。
|
|
163
|
+
- **优先使用 `requestIframeEndpoint()`**:适用于需要 **双向通信**(双方都需要 `send()` + `on()/use()/map()`),或者你希望用一个门面对象更方便地串起全链路做调试。
|
|
164
|
+
|
|
133
165
|
---
|
|
134
166
|
|
|
135
167
|
## 实现原理
|
|
@@ -151,7 +183,7 @@ request-iframe 在 `postMessage` 基础上实现了一套类 HTTP 的通信协
|
|
|
151
183
|
│ │
|
|
152
184
|
│ <──── RESPONSE ──────────────────────── │ 返回结果
|
|
153
185
|
│ │
|
|
154
|
-
│ ────
|
|
186
|
+
│ ──── ACK (可选) ───────────────────────> │ 确认收到响应
|
|
155
187
|
│ │
|
|
156
188
|
```
|
|
157
189
|
|
|
@@ -160,11 +192,10 @@ request-iframe 在 `postMessage` 基础上实现了一套类 HTTP 的通信协
|
|
|
160
192
|
| 类型 | 方向 | 说明 |
|
|
161
193
|
|------|------|------|
|
|
162
194
|
| `request` | Client → Server | 客户端发起请求 |
|
|
163
|
-
| `ack` |
|
|
195
|
+
| `ack` | 接收方 → 发送方 | 回执确认(当消息 `requireAck: true` 且被接管/处理时发送;ACK-only) |
|
|
164
196
|
| `async` | Server → Client | 通知客户端这是异步任务(handler 返回 Promise 时发送) |
|
|
165
197
|
| `response` | Server → Client | 返回响应数据 |
|
|
166
198
|
| `error` | Server → Client | 返回错误信息 |
|
|
167
|
-
| `received` | Client → Server | 客户端确认收到响应/错误(可选,由响应的 `requireAck` 控制) |
|
|
168
199
|
| `ping` | Client → Server | 连接检测(`isConnect()`;可使用 `requireAck` 确认投递) |
|
|
169
200
|
| `pong` | Server → Client | 连接检测响应 |
|
|
170
201
|
|
|
@@ -286,6 +317,29 @@ server.on('/event', (req, res) => {
|
|
|
286
317
|
});
|
|
287
318
|
```
|
|
288
319
|
|
|
320
|
+
### 弹窗 / 新标签页(Window 通信)
|
|
321
|
+
|
|
322
|
+
`request-iframe` 不仅可以与 iframe 通信,也可以把 `target` 直接传 `Window`(例如弹窗/新标签页)。
|
|
323
|
+
|
|
324
|
+
**重要前提**:你必须拿到对方页面的 `Window` 引用(例如 `window.open()` 的返回值,或通过 `window.opener` / `MessageEvent.source` 获取)。**无法**通过 URL 给“任意标签页”发消息。
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// 父页面:打开新标签页/弹窗
|
|
328
|
+
const child = window.open('https://child.example.com/page.html', '_blank');
|
|
329
|
+
if (!child) throw new Error('弹窗被拦截');
|
|
330
|
+
|
|
331
|
+
// 父 -> 子
|
|
332
|
+
const client = requestIframeClient(child, {
|
|
333
|
+
secretKey: 'popup-demo',
|
|
334
|
+
targetOrigin: 'https://child.example.com' // 强烈建议不要用 '*'
|
|
335
|
+
});
|
|
336
|
+
await client.send('/api/ping', { from: 'parent' });
|
|
337
|
+
|
|
338
|
+
// 子页面:创建 server
|
|
339
|
+
const server = requestIframeServer({ secretKey: 'popup-demo' });
|
|
340
|
+
server.on('/api/ping', (req, res) => res.send({ ok: true, echo: req.body }));
|
|
341
|
+
```
|
|
342
|
+
|
|
289
343
|
### 跨域数据获取
|
|
290
344
|
|
|
291
345
|
当 iframe 与父页面不同域时,使用 request-iframe 安全地获取数据:
|
|
@@ -583,6 +637,8 @@ server.on('/api/data', (req, res) => {
|
|
|
583
637
|
|
|
584
638
|
### 文件传输
|
|
585
639
|
|
|
640
|
+
> 说明:文件传输(无论 Client→Server 还是 Server→Client)底层都会通过 stream 协议承载;你只需要使用 `client.sendFile()` / `res.sendFile()` 这一层 API 即可。
|
|
641
|
+
|
|
586
642
|
```typescript
|
|
587
643
|
// Server 端发送文件
|
|
588
644
|
server.on('/api/download', async (req, res) => {
|
|
@@ -615,7 +671,7 @@ if (response.data instanceof File || response.data instanceof Blob) {
|
|
|
615
671
|
|
|
616
672
|
#### Client → Server(Client 向 Server 发送文件)
|
|
617
673
|
|
|
618
|
-
Client
|
|
674
|
+
Client 端发送文件使用 `sendFile()`(或直接 `send(path, file)`);Server 端在 `autoResolve: true`(默认)时会把文件自动解析成 `File/Blob` 放到 `req.body`,当 `autoResolve: false` 时则通过 `req.stream` / `req.body` 暴露为 `IframeFileReadableStream`。
|
|
619
675
|
|
|
620
676
|
```typescript
|
|
621
677
|
// Client 端:发送文件(stream,autoResolve 默认 true)
|
|
@@ -639,14 +695,74 @@ server.on('/api/upload', async (req, res) => {
|
|
|
639
695
|
});
|
|
640
696
|
```
|
|
641
697
|
|
|
642
|
-
**提示**:当 `client.send()` 的 `body` 是 `File/Blob` 时,会自动分发到 `client.sendFile()
|
|
698
|
+
**提示**:当 `client.send()` 的 `body` 是 `File/Blob` 时,会自动分发到 `client.sendFile()`。`autoResolve` 为 true(默认)时 Server 拿到 `req.body`(File/Blob),为 false 时拿到 `req.stream` / `req.body`(`IframeFileReadableStream`)。
|
|
643
699
|
|
|
644
700
|
### 流式传输(Stream)
|
|
645
701
|
|
|
646
|
-
|
|
702
|
+
Stream 除了用于大文件/分块传输,也可以用于“长连接 / 订阅式交互”(类似 SSE / WebSocket,但基于 `postMessage`)。常见用法有两类:
|
|
703
|
+
|
|
704
|
+
- **长连接/订阅**:Client 发起一次请求拿到 `response.stream`,然后用 `for await` 持续消费事件;需要结束时调用 `stream.cancel()`。
|
|
705
|
+
- **分块/文件流**:按 chunk 传输数据或文件(下方示例)。
|
|
706
|
+
|
|
707
|
+
> 长连接注意事项:
|
|
708
|
+
> - `IframeWritableStream` 默认会使用 `asyncTimeout` 作为 `expireTimeout`(避免泄露)。如果你的订阅需要更久,请显式设置更大的 `expireTimeout`,或设置 `expireTimeout: 0` 关闭自动过期(建议配合业务侧取消/重连,避免泄露)。
|
|
709
|
+
> - Server 端的 `res.sendStream(stream)` 会一直等待到流结束;如果你需要在后续主动 `write()` 推送数据,请不要直接 `await` 它,可以用 `void res.sendStream(stream)` 或保存返回的 Promise。
|
|
710
|
+
> - 如果启用了 `maxConcurrentRequestsPerClient`,一个长连接 stream 会占用一个并发槽,需要按业务场景调整。
|
|
711
|
+
> - **事件订阅**:stream 支持 `stream.on(event, listener)`(返回取消订阅函数),可用于埋点/进度/调试(如监听 `start/data/read/write/cancel/end/error/timeout/expired`)。主消费仍建议使用 `for await`。
|
|
712
|
+
|
|
713
|
+
#### 长连接 / 订阅式交互(push 模式示例)
|
|
647
714
|
|
|
648
715
|
```typescript
|
|
649
|
-
|
|
716
|
+
/**
|
|
717
|
+
* Server 端:订阅(长连接)
|
|
718
|
+
* - mode: 'push':由写侧主动 write()
|
|
719
|
+
* - expireTimeout: 0:关闭自动过期(谨慎使用,建议结合业务取消/重连)
|
|
720
|
+
*/
|
|
721
|
+
server.on('/api/subscribe', (req, res) => {
|
|
722
|
+
const stream = new IframeWritableStream({
|
|
723
|
+
type: 'data',
|
|
724
|
+
chunked: true,
|
|
725
|
+
mode: 'push',
|
|
726
|
+
expireTimeout: 0,
|
|
727
|
+
/** 可选:写侧空闲检测(等待 pull/ack 太久会做心跳并失败) */
|
|
728
|
+
streamTimeout: 15000
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
/** 注意:不要 await,否则会一直等到流结束 */
|
|
732
|
+
void res.sendStream(stream);
|
|
733
|
+
|
|
734
|
+
const timer = setInterval(() => {
|
|
735
|
+
try {
|
|
736
|
+
stream.write({ type: 'tick', ts: Date.now() });
|
|
737
|
+
} catch {
|
|
738
|
+
clearInterval(timer);
|
|
739
|
+
}
|
|
740
|
+
}, 1000);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Client 端:持续读取(长连接建议用 for await,而不是 readAll())
|
|
745
|
+
*/
|
|
746
|
+
const resp = await client.send('/api/subscribe', {});
|
|
747
|
+
if (isIframeReadableStream(resp.stream)) {
|
|
748
|
+
/** 事件订阅示例(可选) */
|
|
749
|
+
const off = resp.stream.on(StreamEvent.ERROR, ({ error }) => {
|
|
750
|
+
console.error('stream error:', error);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
for await (const evt of resp.stream) {
|
|
754
|
+
console.log('event:', evt);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
off();
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
#### 分块 / 文件流示例
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
import {
|
|
765
|
+
StreamEvent,
|
|
650
766
|
IframeWritableStream,
|
|
651
767
|
IframeFileWritableStream,
|
|
652
768
|
isIframeReadableStream,
|
|
@@ -757,10 +873,10 @@ if (isIframeFileReadableStream(fileResponse.stream)) {
|
|
|
757
873
|
|
|
758
874
|
| 类型 | 说明 |
|
|
759
875
|
|------|------|
|
|
760
|
-
| `IframeWritableStream` |
|
|
761
|
-
| `IframeFileWritableStream` |
|
|
762
|
-
| `IframeReadableStream` |
|
|
763
|
-
| `IframeFileReadableStream` |
|
|
876
|
+
| `IframeWritableStream` | 写侧(生产者)流:**谁要发送 stream,谁就创建它**;可用于 Server→Client 的响应流,也可用于 Client→Server 的请求流 |
|
|
877
|
+
| `IframeFileWritableStream` | 文件写侧(生产者)流:用于发送文件(底层会做 Base64 编码) |
|
|
878
|
+
| `IframeReadableStream` | 读侧(消费者)流:用于接收普通数据(无论来自 Server 还是 Client) |
|
|
879
|
+
| `IframeFileReadableStream` | 文件读侧(消费者)流:用于接收文件(底层会做 Base64 解码) |
|
|
764
880
|
|
|
765
881
|
> **注意**:文件流内部会进行 Base64 编/解码。Base64 会带来约 33% 的体积膨胀,并且在超大文件场景下可能会有较高的内存/CPU 开销。大文件建议使用 **分块** 文件流(`chunked: true`),并控制 chunk 大小(例如 256KB–1MB)。
|
|
766
882
|
|
|
@@ -775,18 +891,22 @@ interface WritableStreamOptions {
|
|
|
775
891
|
streamTimeout?: number; // 写侧空闲超时(ms,可选):长时间未收到对端 pull/ack 时会做心跳确认并失败
|
|
776
892
|
iterator?: () => AsyncGenerator; // 数据生成迭代器
|
|
777
893
|
next?: () => Promise<{ data: any; done: boolean }>; // 数据生成函数
|
|
894
|
+
maxPendingChunks?: number; // 写侧待发送缓冲上限(可选;push/长连接场景建议配置,避免 pendingQueue 无限增长)
|
|
895
|
+
maxPendingBytes?: number; // 写侧待发送字节上限(可选;避免单次 write 超大 chunk 导致内存暴涨)
|
|
778
896
|
metadata?: Record<string, any>; // 自定义元数据
|
|
779
897
|
}
|
|
780
898
|
```
|
|
781
899
|
|
|
782
900
|
**流超时/保活:**
|
|
783
|
-
- `streamTimeout`(请求参数):读侧空闲超时(ms,可选)。消费 `response.stream` 时超过该时间未收到新的 chunk
|
|
784
|
-
- `streamTimeout`(流参数):写侧空闲超时(ms,可选)。写侧在 pull
|
|
901
|
+
- `streamTimeout`(请求参数):读侧空闲超时(ms,可选)。消费 `response.stream` 时超过该时间未收到新的 chunk,会先做一次心跳确认(默认使用 `client.isConnect()`),失败则认为流已断开并报错。
|
|
902
|
+
- `streamTimeout`(流参数):写侧空闲超时(ms,可选)。写侧在 pull 协议下,若长时间未收到对端 `pull`,会做心跳确认并失败(避免长时间无效占用)。
|
|
785
903
|
- `expireTimeout`(流参数):写侧有效期;过期后会发送 `stream_error`,读侧会收到明确的“已过期”错误。
|
|
904
|
+
- `maxPendingChunks`(流参数):写侧待发送缓冲上限(可选)。对 `push` / 长连接场景很重要:当对端停止 pull 时,继续 `write()` 会在写侧积压,建议设置上限防止内存无限增长。
|
|
905
|
+
- `maxPendingBytes`(流参数):写侧待发送字节上限(可选)。用于防止单次写入超大 chunk(例如大字符串/大 Blob 包装)导致内存占用过高。
|
|
786
906
|
|
|
787
907
|
**pull/ack 协议(新增,默认启用):**
|
|
788
|
-
- 读侧会自动发送 `stream_pull` 请求更多 chunk
|
|
789
|
-
-
|
|
908
|
+
- 读侧会自动发送 `stream_pull` 请求更多 chunk;写侧只会在收到 `stream_pull` 后才继续发送 `stream_data`,实现真正的背压(按需拉取)。
|
|
909
|
+
- 断连检测不依赖“逐帧确认专用消息类型”,而是通过 `streamTimeout + 心跳(isConnect)` 来实现。
|
|
790
910
|
|
|
791
911
|
**consume 默认行为(变更):**
|
|
792
912
|
- `for await (const chunk of stream)` 默认会 **消费并丢弃已迭代过的 chunk**(`consume: true`),避免长流场景内存无限增长。
|
|
@@ -811,9 +931,9 @@ Server 可以要求 Client 确认收到响应:
|
|
|
811
931
|
```typescript
|
|
812
932
|
server.on('/api/important', async (req, res) => {
|
|
813
933
|
// requireAck: true 表示需要客户端确认
|
|
814
|
-
const
|
|
934
|
+
const acked = await res.send(data, { requireAck: true });
|
|
815
935
|
|
|
816
|
-
if (
|
|
936
|
+
if (acked) {
|
|
817
937
|
console.log('客户端已确认收到');
|
|
818
938
|
} else {
|
|
819
939
|
console.log('客户端未确认(超时)');
|
|
@@ -821,21 +941,25 @@ server.on('/api/important', async (req, res) => {
|
|
|
821
941
|
});
|
|
822
942
|
```
|
|
823
943
|
|
|
824
|
-
> **说明**:当响应/错误被客户端“接管”(即存在对应的 pending request)时,库会自动发送 `
|
|
944
|
+
> **说明**:当响应/错误被客户端“接管”(即存在对应的 pending request)时,库会自动发送 `ack`,无需业务侧手动发送。
|
|
825
945
|
|
|
826
946
|
### 追踪模式
|
|
827
947
|
|
|
828
|
-
|
|
948
|
+
默认情况下,request-iframe 只会输出 **warn/error** 日志(避免生产环境控制台过于吵闹)。
|
|
949
|
+
|
|
950
|
+
开启追踪模式(或设置日志等级)可以在控制台查看更详细的通信日志:
|
|
829
951
|
|
|
830
952
|
```typescript
|
|
953
|
+
import { LogLevel } from 'request-iframe';
|
|
954
|
+
|
|
831
955
|
const client = requestIframeClient(iframe, {
|
|
832
956
|
secretKey: 'demo',
|
|
833
|
-
trace: true
|
|
957
|
+
trace: true // 等价于 LogLevel.TRACE
|
|
834
958
|
});
|
|
835
959
|
|
|
836
960
|
const server = requestIframeServer({
|
|
837
961
|
secretKey: 'demo',
|
|
838
|
-
trace:
|
|
962
|
+
trace: LogLevel.INFO // 输出 info/warn/error(比 trace 更克制)
|
|
839
963
|
});
|
|
840
964
|
|
|
841
965
|
// 控制台输出:
|
|
@@ -844,6 +968,14 @@ const server = requestIframeServer({
|
|
|
844
968
|
// [request-iframe] [INFO] ✅ Request Success { status: 200, data: {...} }
|
|
845
969
|
```
|
|
846
970
|
|
|
971
|
+
`trace` 支持:
|
|
972
|
+
- `true` / `false`
|
|
973
|
+
- `'trace' | 'info' | 'warn' | 'error' | 'silent'`(或 `LogLevel.*`)
|
|
974
|
+
|
|
975
|
+
说明:
|
|
976
|
+
- 当 `trace` 为 `LogLevel.TRACE` / `LogLevel.INFO` 时,库会额外挂载内置的调试拦截器/监听器,以输出更丰富的 request/response 日志。
|
|
977
|
+
- 当 `trace` 为 `LogLevel.WARN` / `LogLevel.ERROR` / `LogLevel.SILENT` 时,只影响日志输出等级(不会额外挂载调试拦截器)。
|
|
978
|
+
|
|
847
979
|
### 多语言支持
|
|
848
980
|
|
|
849
981
|
```typescript
|
|
@@ -874,7 +1006,7 @@ setMessages({
|
|
|
874
1006
|
|------|------|------|
|
|
875
1007
|
| `target` | `HTMLIFrameElement \| Window` | 目标 iframe 元素或 window 对象 |
|
|
876
1008
|
| `options.secretKey` | `string` | 消息隔离标识(可选) |
|
|
877
|
-
| `options.trace` | `boolean` |
|
|
1009
|
+
| `options.trace` | `boolean \| 'trace' \| 'info' \| 'warn' \| 'error' \| 'silent'` | trace/日志等级(可选)。默认只输出 warn/error |
|
|
878
1010
|
| `options.targetOrigin` | `string` | 覆盖 postMessage 的 targetOrigin(可选)。当 `target` 是 `Window` 时默认 `*`;当 `target` 是 iframe 时默认取 `iframe.src` 的 origin。 |
|
|
879
1011
|
| `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认 1000 |
|
|
880
1012
|
| `options.timeout` | `number` | 全局默认请求超时(ms),默认 5000 |
|
|
@@ -886,6 +1018,70 @@ setMessages({
|
|
|
886
1018
|
|
|
887
1019
|
**返回值:** `RequestIframeClient`
|
|
888
1020
|
|
|
1021
|
+
**关于 `target: Window` 的说明:**
|
|
1022
|
+
- **必须持有对方页面的 `Window` 引用**(例如 `window.open()` 返回值、`window.opener`、或 `MessageEvent.source`)。
|
|
1023
|
+
- **无法**通过 URL 给“任意标签页”发消息。
|
|
1024
|
+
- 安全起见,建议显式设置 `targetOrigin`,并配置 `allowedOrigins` / `validateOrigin`。
|
|
1025
|
+
|
|
1026
|
+
**生产环境推荐配置(模板):**
|
|
1027
|
+
|
|
1028
|
+
```typescript
|
|
1029
|
+
import { requestIframeClient, requestIframeServer } from 'request-iframe';
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* 建议:明确限定 3 件事
|
|
1033
|
+
* - secretKey:隔离不同业务/不同实例,避免消息串线
|
|
1034
|
+
* - targetOrigin:发送时的 targetOrigin(Window 场景强烈建议不要用 '*')
|
|
1035
|
+
* - allowedOrigins / validateOrigin:接收时的 origin 白名单校验
|
|
1036
|
+
*/
|
|
1037
|
+
const secretKey = 'my-app';
|
|
1038
|
+
const targetOrigin = 'https://child.example.com';
|
|
1039
|
+
const allowedOrigins = [targetOrigin];
|
|
1040
|
+
|
|
1041
|
+
// Client(父页)
|
|
1042
|
+
const client = requestIframeClient(window.open(targetOrigin)!, {
|
|
1043
|
+
secretKey,
|
|
1044
|
+
targetOrigin,
|
|
1045
|
+
allowedOrigins
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
// Server(子页/iframe 内)
|
|
1049
|
+
const server = requestIframeServer({
|
|
1050
|
+
secretKey,
|
|
1051
|
+
allowedOrigins,
|
|
1052
|
+
// 防止异常/攻击导致消息爆炸(按需设置)
|
|
1053
|
+
maxConcurrentRequestsPerClient: 50
|
|
1054
|
+
});
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**生产环境推荐配置(iframe 场景模板):**
|
|
1058
|
+
|
|
1059
|
+
```typescript
|
|
1060
|
+
import { requestIframeClient, requestIframeServer } from 'request-iframe';
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* iframe 场景通常可以从 iframe.src 推导 targetOrigin,并用它作为 allowedOrigins 白名单。
|
|
1064
|
+
*/
|
|
1065
|
+
const iframe = document.querySelector('iframe')!;
|
|
1066
|
+
const targetOrigin = new URL(iframe.src).origin;
|
|
1067
|
+
const secretKey = 'my-app';
|
|
1068
|
+
|
|
1069
|
+
// Client(父页)
|
|
1070
|
+
const client = requestIframeClient(iframe, {
|
|
1071
|
+
secretKey,
|
|
1072
|
+
// 可显式写出来(即使库内部也会默认推导),便于审计/避免误用 '*'
|
|
1073
|
+
targetOrigin,
|
|
1074
|
+
allowedOrigins: [targetOrigin]
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
// Server(iframe 内)
|
|
1078
|
+
const server = requestIframeServer({
|
|
1079
|
+
secretKey,
|
|
1080
|
+
allowedOrigins: [targetOrigin],
|
|
1081
|
+
maxConcurrentRequestsPerClient: 50
|
|
1082
|
+
});
|
|
1083
|
+
```
|
|
1084
|
+
|
|
889
1085
|
**示例:**
|
|
890
1086
|
|
|
891
1087
|
```typescript
|
|
@@ -912,7 +1108,7 @@ await client.send('/api/longTask', {}, {
|
|
|
912
1108
|
| 参数 | 类型 | 说明 |
|
|
913
1109
|
|------|------|------|
|
|
914
1110
|
| `options.secretKey` | `string` | 消息隔离标识(可选) |
|
|
915
|
-
| `options.trace` | `boolean` |
|
|
1111
|
+
| `options.trace` | `boolean \| 'trace' \| 'info' \| 'warn' \| 'error' \| 'silent'` | trace/日志等级(可选)。默认只输出 warn/error |
|
|
916
1112
|
| `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认 1000 |
|
|
917
1113
|
| `options.maxConcurrentRequestsPerClient` | `number` | 每个客户端的最大并发 in-flight 请求数(按 origin + creatorId 维度),默认 Infinity |
|
|
918
1114
|
| `options.allowedOrigins` | `string \| RegExp \| Array<string \| RegExp>` | 接收消息的 origin 白名单(可选,生产环境强烈建议配置) |
|
|
@@ -920,6 +1116,65 @@ await client.send('/api/longTask', {}, {
|
|
|
920
1116
|
|
|
921
1117
|
**返回值:** `RequestIframeServer`
|
|
922
1118
|
|
|
1119
|
+
### requestIframeEndpoint(target, options?)
|
|
1120
|
+
|
|
1121
|
+
创建一个 **endpoint 门面**(client + server)并绑定到某个对端窗口/iframe。
|
|
1122
|
+
|
|
1123
|
+
它可以:
|
|
1124
|
+
- **向对端发送请求**:`endpoint.send(...)`
|
|
1125
|
+
- **处理对端发来的请求**:`endpoint.on(...)` / `endpoint.use(...)` / `endpoint.map(...)`
|
|
1126
|
+
|
|
1127
|
+
说明:
|
|
1128
|
+
- 内部的 client/server 是 **懒创建** 的(只有首次使用发送/注册 handler 等能力时才会创建)。
|
|
1129
|
+
- 如果传了 `options.id`,它会作为 client+server 的共享 id;不传则会自动生成一个。
|
|
1130
|
+
- `options.trace` 与 client/server 一致,推荐用 `LogLevel.*` 来配置日志等级。
|
|
1131
|
+
|
|
1132
|
+
示例(使用 endpoint 做双向通信,推荐):
|
|
1133
|
+
|
|
1134
|
+
```typescript
|
|
1135
|
+
import { requestIframeEndpoint, LogLevel } from 'request-iframe';
|
|
1136
|
+
|
|
1137
|
+
// 父页面(持有 iframe 元素)
|
|
1138
|
+
const iframe = document.querySelector('iframe')!;
|
|
1139
|
+
const parentEndpoint = requestIframeEndpoint(iframe, {
|
|
1140
|
+
secretKey: 'demo',
|
|
1141
|
+
trace: LogLevel.INFO
|
|
1142
|
+
});
|
|
1143
|
+
parentEndpoint.on('/notify', (req, res) => res.send({ ok: true, echo: req.body }));
|
|
1144
|
+
|
|
1145
|
+
// iframe 页面(持有 window.parent)
|
|
1146
|
+
const iframeEndpoint = requestIframeEndpoint(window.parent, {
|
|
1147
|
+
secretKey: 'demo',
|
|
1148
|
+
targetOrigin: 'https://parent.example.com',
|
|
1149
|
+
trace: true
|
|
1150
|
+
});
|
|
1151
|
+
iframeEndpoint.on('/api/ping', (req, res) => res.send({ ok: true }));
|
|
1152
|
+
|
|
1153
|
+
// 任意一侧都可以 send + handle
|
|
1154
|
+
await parentEndpoint.send('/api/ping', { from: 'parent' });
|
|
1155
|
+
await iframeEndpoint.send('/notify', { from: 'iframe' });
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
生产环境推荐配置(模板):
|
|
1159
|
+
|
|
1160
|
+
```typescript
|
|
1161
|
+
import { requestIframeEndpoint, LogLevel } from 'request-iframe';
|
|
1162
|
+
|
|
1163
|
+
const secretKey = 'my-app';
|
|
1164
|
+
const iframe = document.querySelector('iframe')!;
|
|
1165
|
+
const targetOrigin = new URL(iframe.src).origin;
|
|
1166
|
+
|
|
1167
|
+
const endpoint = requestIframeEndpoint(iframe, {
|
|
1168
|
+
secretKey,
|
|
1169
|
+
targetOrigin,
|
|
1170
|
+
allowedOrigins: [targetOrigin],
|
|
1171
|
+
// 防止异常/攻击导致消息爆炸(按需设置)
|
|
1172
|
+
maxConcurrentRequestsPerClient: 50,
|
|
1173
|
+
// 日志:默认只输出 warn/error;调试时可切到 LogLevel.INFO / LogLevel.TRACE
|
|
1174
|
+
trace: LogLevel.WARN
|
|
1175
|
+
});
|
|
1176
|
+
```
|
|
1177
|
+
|
|
923
1178
|
### Client API
|
|
924
1179
|
|
|
925
1180
|
#### client.send(path, body?, options?)
|
|
@@ -1520,7 +1775,10 @@ import {
|
|
|
1520
1775
|
// 多语言消息
|
|
1521
1776
|
Messages,
|
|
1522
1777
|
setMessages,
|
|
1523
|
-
formatMessage
|
|
1778
|
+
formatMessage,
|
|
1779
|
+
|
|
1780
|
+
// 日志等级
|
|
1781
|
+
LogLevel
|
|
1524
1782
|
} from 'request-iframe';
|
|
1525
1783
|
```
|
|
1526
1784
|
|
|
@@ -1611,7 +1869,11 @@ const server = requestIframeServer();
|
|
|
1611
1869
|
|
|
1612
1870
|
### 4. Server 可以主动推送消息吗?
|
|
1613
1871
|
|
|
1614
|
-
request-iframe 是请求-响应模式,Server
|
|
1872
|
+
request-iframe 是请求-响应模式,Server 本身不能“主动推送”。
|
|
1873
|
+
|
|
1874
|
+
如需双向通信,有两种做法:
|
|
1875
|
+
- iframe 内创建一个反向的 Client(传统做法)
|
|
1876
|
+
- 双方都使用 `requestIframeEndpoint()`(推荐),一个对象同时具备 **send + handle**
|
|
1615
1877
|
|
|
1616
1878
|
```typescript
|
|
1617
1879
|
// iframe 内
|
|
@@ -1624,10 +1886,11 @@ await client.send('/notify', { event: 'data-changed' });
|
|
|
1624
1886
|
|
|
1625
1887
|
### 5. 如何调试通信问题?
|
|
1626
1888
|
|
|
1627
|
-
1.
|
|
1628
|
-
2. **检查 secretKey
|
|
1889
|
+
1. **按日志等级开启输出**:默认只输出 warn/error;建议设置 `trace: LogLevel.INFO`(或 `trace: true`)来输出更详细的通信日志
|
|
1890
|
+
2. **检查 secretKey**:确保双方使用相同的 `secretKey`
|
|
1629
1891
|
3. **检查 iframe 加载**:确保 iframe 已完全加载
|
|
1630
|
-
4.
|
|
1892
|
+
4. **检查 origin 约束**:尽量设置严格的 `targetOrigin`,并配置 `allowedOrigins` / `validateOrigin`,避免因为校验失败导致消息被忽略
|
|
1893
|
+
5. **考虑使用 `requestIframeEndpoint()`**:把双向(send + handle)能力合在一个对象里,更容易串起完整链路做排查
|
|
1631
1894
|
|
|
1632
1895
|
### 6. 支持哪些浏览器?
|
|
1633
1896
|
|
|
@@ -1681,27 +1944,10 @@ client.interceptors.response.use(
|
|
|
1681
1944
|
);
|
|
1682
1945
|
```
|
|
1683
1946
|
|
|
1684
|
-
### 9.
|
|
1685
|
-
|
|
1686
|
-
1. **开启 trace 模式**:在创建 client/server 时设置 `trace: true`
|
|
1687
|
-
2. **检查控制台**:查看详细的通信日志
|
|
1688
|
-
3. **验证 secretKey**:确保 client 和 server 使用相同的 secretKey
|
|
1689
|
-
4. **检查 iframe 加载**:确保 iframe 已完全加载后再发送请求
|
|
1690
|
-
5. **使用 `isConnect()`**:先检测连接是否正常
|
|
1691
|
-
|
|
1692
|
-
```typescript
|
|
1693
|
-
// 开启调试模式
|
|
1694
|
-
const client = requestIframeClient(iframe, {
|
|
1695
|
-
secretKey: 'my-app',
|
|
1696
|
-
trace: true // 开启详细日志
|
|
1697
|
-
});
|
|
1947
|
+
### 9. trace/日志等级怎么用?
|
|
1698
1948
|
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
if (!connected) {
|
|
1702
|
-
console.error('无法连接到 iframe');
|
|
1703
|
-
}
|
|
1704
|
-
```
|
|
1949
|
+
- 推荐优先使用常量:`trace: LogLevel.INFO` / `trace: LogLevel.TRACE`
|
|
1950
|
+
- 如果你在做双向排查,推荐使用 `requestIframeEndpoint()` 并把 trace 打开(这样 send/handle 都在同一对象上更直观)
|
|
1705
1951
|
|
|
1706
1952
|
### 10. 性能如何?
|
|
1707
1953
|
|
|
@@ -1775,14 +2021,16 @@ yarn build
|
|
|
1775
2021
|
request-iframe/
|
|
1776
2022
|
├── src/
|
|
1777
2023
|
│ ├── api/ # 对外 API(client.ts, server.ts)
|
|
1778
|
-
│ ├──
|
|
2024
|
+
│ ├── impl/ # 实现层(client, server, request, response)
|
|
2025
|
+
│ ├── endpoint/ # endpoint 基础设施(hub/inbox/outbox + stream/heartbeat 等)
|
|
1779
2026
|
│ ├── message/ # 消息通信层(channel, dispatcher)
|
|
1780
2027
|
│ ├── stream/ # 流式传输实现
|
|
1781
2028
|
│ ├── interceptors/ # 拦截器实现
|
|
1782
2029
|
│ ├── utils/ # 工具函数
|
|
1783
2030
|
│ ├── constants/ # 常量定义
|
|
1784
2031
|
│ ├── types/ # TypeScript 类型定义
|
|
1785
|
-
|
|
2032
|
+
├── __tests__/ # 测试文件(Jest)
|
|
2033
|
+
├── react/__tests__/ # React hooks 测试
|
|
1786
2034
|
├── library/ # 构建输出
|
|
1787
2035
|
├── coverage/ # 测试覆盖率报告
|
|
1788
2036
|
├── jest.config.js # Jest 配置
|