1. 文档说明
本文档描述业务 iframe 与宿主父页面之间的存储通信协议。iframe 不直接操作宿主页面的业务存储,而是通过浏览器
postMessage API 请求父页面代为保存或查询数据。适用场景:- 子页面运行在
<iframe>中; - 父页面负责统一管理 iframe 数据;
- 数据最终写入浏览器
localStorage。
说明:原 HTML 中提供了 iframe 侧的发送和接收逻辑;父页面的消息处理器需要由接入方实现。
2. 通信模型
2.1 通信方向
| 方向 | API | 用途 |
| iframe → 父页面 | window.parent.postMessage(message, targetOrigin) | 发起存储或查询请求 |
| 父页面 → iframe | window.postMessage(message, targetOrigin) | 返回处理结果 |
iframe 通过以下方式监听父页面回复:
window.addEventListener('message', handler)2.2 前置握手消息
iframe 加载完成后会主动向父页面发送:
{
"hello": true
}该消息用于通知父页面 iframe 已加载。原 Demo 未定义对应响应,父页面可按需记录或忽略。
2.3 消息格式约定
所有消息均为 JavaScript 对象,通过
postMessage 传递。协议使用 type 字段区分消息类型。请求和响应中的 key 应保持一致。3. 存储数据
3.1 请求
消息类型:
set-storagewindow.parent.postMessage({
type: 'set-storage',
key: 'userName',
value: '张三'
}, '*')字段说明:
| 字段 | 类型 | 必填 | 说明 |
type | string | 是 | 固定值 "set-storage" |
key | string | 是 | 存储键名,必须符合第 5 节校验规则 |
value | any | 否 | 要存储的值,可为字符串、数字、对象、数组等 |
3.2 成功响应
消息类型:
storage-set{
"type": "storage-set",
"key": "userName",
"success": true,
"message": "userName stored successfully"
}| 字段 | 类型 | 说明 |
type | string | 固定值 "storage-set" |
key | string | 请求中的键名 |
success | boolean | 是否存储成功;成功时为 true |
message | string | 处理结果说明 |
3.3 失败响应
失败时仍返回
type: "storage-set",通过 success: false 表示失败,message 返回具体原因。例如:{
"type": "storage-set",
"key": "userName",
"success": false,
"message": "value too large, max 100KB"
}4. 查询数据
4.1 请求
消息类型:
get-storagewindow.parent.postMessage({
type: 'get-storage',
key: 'userName'
}, '*')字段说明:
| 字段 | 类型 | 必填 | 说明 |
type | string | 是 | 固定值 "get-storage" |
key | string | 是 | 要查询的键名 |
4.2 查询响应
消息类型:
storage-get存在数据
{
"type": "storage-get",
"key": "userName",
"exists": true,
"value": "张三"
}不存在数据
{
"type": "storage-get",
"key": "missingKey",
"exists": false,
"value": null,
"message": "not found"
}| 字段 | 类型 | 说明 |
type | string | 固定值 "storage-get" |
key | string | 请求中的键名 |
exists | boolean | 是否存在该键;不存在时为 false |
value | any | 存在时返回存储值;不存在时为 null |
message | string | 不存在时通常为 "not found";存在时可不返回 |
5. 数据校验与容量限制
父页面在处理
set-storage 请求时应执行以下校验:| 校验项 | 限制 | 失败时的 message |
| Key 类型及必填 | key 必须存在且为 string | invalid key: key is required and must be a string |
| Key 字符 | 仅允许 A-Z、a-z、0-9、_、.、- | invalid key: only letters, numbers, underscore, dot, hyphen allowed |
| Key 长度 | 最长 64 个字符 | key too long, max 64 chars |
| 单个 Value 大小 | 最大 100KB | value too large, max 100KB |
| 总存储容量 | 所有 iframe 数据合计最大 2MB | storage quota exceeded, max 2MB total for iframe data |
| Key 数量 | 最多 50 个 | too many keys, max 50 keys allowed |
| localStorage 异常 | 浏览器存储空间不足等异常 | storage failed, localStorage may be full |
Key 校验可参考:
const KEY_PATTERN = /^[A-Za-z0-9_.-]+$/
const valid = typeof key === 'string'
&& key.length <= 64
&& KEY_PATTERN.test(key)所有 iframe 数据写入
localStorage 时使用 iframe_ 前缀,以便与宿主项目其他数据隔离。例如:iframe_userName6. iframe 侧接入示例
function requestSet(key, value) {
if (window.parent === window) {
throw new Error('当前页面不在 iframe 中')
}
window.parent.postMessage({
type: 'set-storage',
key,
value
}, '*')
}
function requestGet(key) {
if (window.parent === window) {
throw new Error('当前页面不在 iframe 中')
}
window.parent.postMessage({
type: 'get-storage',
key
}, '*')
}
window.addEventListener('message', (event) => {
const data = event.data || {}
if (data.type === 'storage-set') {
if (data.success) {
console.log('存储成功:', data.key)
} else {
console.error('存储失败:', data.message)
}
}
if (data.type === 'storage-get') {
if (data.exists) {
console.log('查询结果:', data.value)
} else {
console.log('数据不存在:', data.message || 'not found')
}
}
})7. 父页面接入要求
父页面需要监听
message 事件,识别 set-storage 和 get-storage,完成校验、读写 localStorage,并将结果发回触发请求的 iframe(通常使用 event.source.postMessage)。建议处理流程如下:- 校验消息来源
event.origin和event.source,确认来自受信任 iframe。 - 校验
event.data是否为对象,并检查type、key等字段。 - 对
set-storage执行 key、value、容量和数量校验。 - 使用
iframe_前缀读写数据。 - 按协议返回
storage-set或storage-get响应。
父页面回复示例:
window.addEventListener('message', (event) => {
const data = event.data || {}
if (!event.source || typeof data !== 'object') return
if (data.type === 'get-storage') {
const storageKey = `iframe_${data.key}`
const raw = localStorage.getItem(storageKey)
const response = raw === null
? { type: 'storage-get', key: data.key, exists: false, value: null, message: 'not found' }
: { type: 'storage-get', key: data.key, exists: true, value: JSON.parse(raw) }
event.source.postMessage(response, event.origin)
}
})上述代码仅展示通信方式。生产环境还应补充完整的来源白名单、异常捕获、序列化失败处理和容量统计。
8. 安全注意事项
原 Demo 使用
postMessage(data, '*'),便于本地演示,但生产环境不建议继续使用通配符:- 发送时将
'*'替换为明确的父页面 Origin; - 接收时校验
event.origin是否在允许列表中; - 校验
event.source是否为预期 iframe 的contentWindow; - 不要把密码、Token、身份证号等高敏感信息直接写入
localStorage; - 对
JSON.parse、JSON.stringify和localStorage操作进行异常处理。
9. 错误处理建议
调用方应以响应中的
type 作为第一层判断,以 success 或 exists 作为业务结果判断,以 message 作为日志和排查依据。建议不要仅通过 message 文案判断成功失败,因为文案可能在后续版本中调整。10. 兼容性与边界说明
- 通信依赖浏览器
window.postMessage和message事件。 - 页面直接打开、未嵌入 iframe 时,
window.parent === window,无法向父页面发送请求。 value标注为any,但跨窗口传输和localStorage持久化通常需要进行结构化克隆或 JSON 序列化;父页面应统一约定序列化规则。- 原 HTML 未定义请求 ID、超时、重试和并发关联机制。如存在并发请求同一 key 的场景,建议后续增加
requestId字段。