模板模式嵌入主站
这篇文档专门写给主站宿主页面使用:当主站通过 iframe 内嵌接入了 @atomm-developer/generator-workbench 的生成器,并且需要在模板模式 (路由?mode=embed) 下与 iframe 进行 bridge 通信时,宿主应该如何改。
如果你改的是生成器壳层本身,先看 应用壳功能与配置参考。如果你改的是父页面,也就是拥有 iframe 的主站宿主,直接看这篇。
路由能力模式(?mode=embed)
URL 参数 ?mode=embed 控制的是应用壳的功能面,与 config.mode(外壳布局模式)是两个独立的概念:
| URL 参数 | 应用壳行为 |
|---|---|
无参数 / ?mode=full | 完整壳层能力;顶栏和导出区可见;云保存 · 积分 · 计费 · 邀请全部开启 |
?mode=embed | 精简壳层;顶栏和导出区隐藏;云保存 · 积分 · 计费 · 邀请全部关闭;启动 iframe bridge |
这两个维度是正交的。同一个 config.mode: 'shell' 的生成器,既可以以完整壳层运行,也可以以 ?mode=embed 嵌入到主站模板页。
模板模式(路由?mode=embed) 下到底变了什么
当 iframe 地址带上 ?mode=embed 后,应用壳会有两个关键变化:
- iframe 内会隐藏
.app-topbar和#sidebar-footer这些壳层 DOM。 - iframe 会启动一套 bridge,通过
postMessage和主站通信。
注意:被隐藏的是壳层 DOM,不是底层能力。生成器、导出、模板、bridge 这些能力仍然存在。
宿主必须改哪些地方
主站至少要做下面几件事:
- iframe 地址追加
mode=embed - 在发送 bridge 指令前,先注册父页面的
message监听 - 等待
generator_pageLoaded再发送模板或状态 payload - 模板初始化优先改成
generator_loadTemplateData generator_setGeneratorData只保留给生成器快照恢复- 处理 iframe 回传的成功和失败事件
不要再用 iframe.onload 当 bridge ready
iframe.onload 只能说明浏览器把页面资源加载完了,它不能保证应用壳已经完成生成器挂载,也不能保证 bridge 的 message 监听已经挂好。
对于 bridge 指令来说,唯一的 ready 信号是:
generator_pageLoaded
这个事件只会在 iframe 内的 bridge listener 挂好之后才发给主站。
推荐握手方式
推荐宿主按这个顺序改:
- 创建带
mode=embed的 iframe 地址 - 提前注册主站
message监听 - 先把要发给 iframe 的 payload 缓存在宿主本地
- 等待
generator_pageLoaded - 收到 ready 后再 flush 之前缓存的 payload
- 对模板链路等待
generator_toTemplateLoaded
最小宿主示例
const iframe = document.querySelector('#generator-frame')
const iframeOrigin = new URL(iframe.src, window.location.href).origin
const pendingMessages = []
let bridgeReady = false
function postToIframe(type, data) {
const payload = { type, data }
if (!bridgeReady) {
pendingMessages.push(payload)
return
}
iframe.contentWindow?.postMessage(payload, iframeOrigin)
}
function flushPendingMessages() {
while (pendingMessages.length > 0) {
const payload = pendingMessages.shift()
iframe.contentWindow?.postMessage(payload, iframeOrigin)
}
}
window.addEventListener('message', (event) => {
if (event.origin !== iframeOrigin) {
return
}
const message = event.data || {}
switch (message.type) {
case 'generator_pageLoaded':
bridgeReady = true
flushPendingMessages()
return
case 'generator_toTemplateLoaded':
console.log('template applied', message.data)
return
case 'generator_toSelectTemplate':
console.log('生成器选中了模板', message.data)
return
case 'generator_toTemplateError':
console.error('bridge template error', message.data)
return
case 'generator_toFile':
console.log('file payload', message.data)
return
}
})
postToIframe('generator_loadTemplateData', {
title: 'Pendant 140x128',
template: {
type: 'generator-template',
version: '1.0.0',
generatorId: 'pendant-generator',
defaults: {
meta: {
generatorId: 'pendant-generator',
schemaVersion: '1.0.0',
},
params: {
height: 150,
width: 128,
},
version: '1.0.0',
},
},
})模板初始化到底该发哪个事件
优先用 generator_loadTemplateData
当宿主希望把模板数据加载进当前生成器时,优先使用 generator_loadTemplateData。
原因是:
- 它直接映射到
sdk.template.applyToRuntime(...) - 它会回传
generator_toTemplateLoaded - 更容易做幂等、重试和超时控制
- 它和应用壳自身"导入模板"的行为一致
谨慎使用 generator_setGeneratorData
当宿主希望把生成器快照(snapshot)样式的数据恢复到当前生成器时,再用 generator_setGeneratorData。
需要明确的协议边界:
- 它不会返回专门的成功 ack
- 如果失败,iframe 会回传
generator_toTemplateError,其中action = 'setGeneratorData' - 因为没有成功 ack,所以它不适合作为主要的模板初始化事件
事件对照表
| 方向 | 事件 | 宿主要做什么 |
|---|---|---|
| iframe -> host | generator_pageLoaded | 标记 bridge ready,并 flush 队列 |
| host -> iframe | generator_loadTemplateData | 把模板数据加载进应用壳 |
| iframe -> host | generator_toTemplateLoaded | 把模板初始化视为完成 |
| host -> iframe | generator_setGeneratorData | 恢复生成器快照数据 |
| iframe -> host | generator_toTemplateError | 处理模板或状态桥接失败 |
| iframe -> host | generator_toSelectTemplate | 接收生成器上报的模板选中事件 |
| host -> iframe | generator_getTemplateData | 向 iframe 拉取当前模板 |
| iframe -> host | generator_toTemplateData | 读取拉回来的模板数据 |
| host -> iframe | generator_getFile | 让 iframe 返回导出文件数据 |
| iframe -> host | generator_toFile | 消费导出文件 payload |
| iframe -> host | generator_toFileError | 处理导出失败 |
生成器侧新增协议
如果 iframe 内部的生成器需要告诉主站"用户点击了某个模板卡片",它可以发出:
emit({
type: 'select_template',
data: {
name: 'Spring Sale',
category: 'marketing',
},
})在 ?mode=embed 下,应用壳会自动把它转发成父页面可监听的 bridge 事件:
generator_toSelectTemplate
错误处理建议
- 主站统一记录
generator_toTemplateError和generator_toFileError - 对
generator_loadTemplateData加超时控制;如果迟迟收不到generator_toTemplateLoaded,视为本次初始化失败 - 如果宿主业务流在 ready 前就产生了 payload,不要直接发,先缓存,等
generator_pageLoaded后再重试 - 联调时临时给 iframe 地址追加
?bridgeDebug=1
推荐迁移步骤
如果你现在的主站逻辑是"iframe 一创建就立刻发消息",推荐按下面顺序改:
- 把父页面
message监听前移 - 在宿主本地增加一个待发送队列
- 用
generator_pageLoaded作为唯一 ready 门槛 - 尽可能把模板初始化从
generator_setGeneratorData改成generator_loadTemplateData - 只有在确实需要恢复生成器快照时,才保留
generator_setGeneratorData