Skip to content

模板模式嵌入主站

这篇文档专门写给主站宿主页面使用:当主站通过 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 后,应用壳会有两个关键变化:

  1. iframe 内会隐藏 .app-topbar#sidebar-footer 这些壳层 DOM。
  2. iframe 会启动一套 bridge,通过 postMessage 和主站通信。

注意:被隐藏的是壳层 DOM,不是底层能力。生成器、导出、模板、bridge 这些能力仍然存在。

宿主必须改哪些地方

主站至少要做下面几件事:

  1. iframe 地址追加 mode=embed
  2. 在发送 bridge 指令前,先注册父页面的 message 监听
  3. 等待 generator_pageLoaded 再发送模板或状态 payload
  4. 模板初始化优先改成 generator_loadTemplateData
  5. generator_setGeneratorData 只保留给生成器快照恢复
  6. 处理 iframe 回传的成功和失败事件

不要再用 iframe.onload 当 bridge ready

iframe.onload 只能说明浏览器把页面资源加载完了,它不能保证应用壳已经完成生成器挂载,也不能保证 bridge 的 message 监听已经挂好。

对于 bridge 指令来说,唯一的 ready 信号是:

  • generator_pageLoaded

这个事件只会在 iframe 内的 bridge listener 挂好之后才发给主站。

推荐握手方式

推荐宿主按这个顺序改:

  1. 创建带 mode=embed 的 iframe 地址
  2. 提前注册主站 message 监听
  3. 先把要发给 iframe 的 payload 缓存在宿主本地
  4. 等待 generator_pageLoaded
  5. 收到 ready 后再 flush 之前缓存的 payload
  6. 对模板链路等待 generator_toTemplateLoaded

最小宿主示例

js
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 -> hostgenerator_pageLoaded标记 bridge ready,并 flush 队列
host -> iframegenerator_loadTemplateData把模板数据加载进应用壳
iframe -> hostgenerator_toTemplateLoaded把模板初始化视为完成
host -> iframegenerator_setGeneratorData恢复生成器快照数据
iframe -> hostgenerator_toTemplateError处理模板或状态桥接失败
iframe -> hostgenerator_toSelectTemplate接收生成器上报的模板选中事件
host -> iframegenerator_getTemplateData向 iframe 拉取当前模板
iframe -> hostgenerator_toTemplateData读取拉回来的模板数据
host -> iframegenerator_getFile让 iframe 返回导出文件数据
iframe -> hostgenerator_toFile消费导出文件 payload
iframe -> hostgenerator_toFileError处理导出失败

生成器侧新增协议

如果 iframe 内部的生成器需要告诉主站"用户点击了某个模板卡片",它可以发出:

ts
emit({
  type: 'select_template',
  data: {
    name: 'Spring Sale',
    category: 'marketing',
  },
})

?mode=embed 下,应用壳会自动把它转发成父页面可监听的 bridge 事件:

  • generator_toSelectTemplate

错误处理建议

  • 主站统一记录 generator_toTemplateErrorgenerator_toFileError
  • generator_loadTemplateData 加超时控制;如果迟迟收不到 generator_toTemplateLoaded,视为本次初始化失败
  • 如果宿主业务流在 ready 前就产生了 payload,不要直接发,先缓存,等 generator_pageLoaded 后再重试
  • 联调时临时给 iframe 地址追加 ?bridgeDebug=1

推荐迁移步骤

如果你现在的主站逻辑是"iframe 一创建就立刻发消息",推荐按下面顺序改:

  1. 把父页面 message 监听前移
  2. 在宿主本地增加一个待发送队列
  3. generator_pageLoaded 作为唯一 ready 门槛
  4. 尽可能把模板初始化从 generator_setGeneratorData 改成 generator_loadTemplateData
  5. 只有在确实需要恢复生成器快照时,才保留 generator_setGeneratorData

相关文档

MIT Licensed