Skip to content

Iframe 通信与缓存

链接型页面支持两种打开方式:

  • 内部打开:作为 iframe 标签页渲染
  • 外部打开:传 _viewOutside: true 后使用 window.open

打开 iframe 标签

ts
import { TabViewUrl } from "@xsbcme/vue-tab-router";

const tabsManager = useTabsManager();

tabsManager.openTab(TabViewUrl.createRelative("/iframe-test.html"), {
  _viewName: "同源 Iframe",
  iframeDemo: true,
});

TabViewUrl.createRelative() 会创建当前系统内的相对 iframe 地址。http / https 地址也会作为链接型页面处理。

缓存与不缓存

iframe 缓存不是 Vue KeepAlive,而是由容器维护一个持久 iframe DOM 层。默认会缓存 iframe:切换到其他标签再回来时,iframe 内部状态会保留。

ts
tabsManager.openTab(TabViewUrl.createRelative("/iframe-test.html"), {
  _viewName: "缓存 Iframe",
});

禁用缓存时,切换离开后会销毁 iframe,回来时重新加载:

ts
tabsManager.openTab(TabViewUrl.createRelative("/iframe-test.html"), {
  _viewName: "不缓存 Iframe",
  _viewNoCache: true,
});

Demo 的 Iframe 通信与样式 页面提供了“缓存 iframe / 不缓存 iframe / 打开组件页用于切换”三步对照。缓存 iframe 的加载标识和页面内计数应保留;不缓存 iframe 切回来会变成新的加载标识。

接收 iframe 消息

初始化时配置允许来源和消息回调:

ts
const tabsManager = createTabsManager({
  views: {
    modules,
  },
  iframe: {
    messageOrigins: ["self"],
    onMessage(message) {
      if (message.data?.type === "iframe:refresh-current") {
        tabsManager.refreshTab(message.tabId);
        message.reply({ type: "host:refreshed" });
      }
    },
  },
});

默认只允许同源消息。跨域 iframe 需要显式配置来源,不建议生产环境使用 "*"

宿主向 iframe 发送消息

向当前激活 iframe 发送:

ts
tabsManager.postActiveIframeMessage({ type: "host:active-message" });

向指定 iframe tab 发送:

ts
tabsManager.postIframeMessage(tabId, { type: "host:tab-id-message" });

在当前页面组件内部免 tabId 发送:

ts
import { postCurrentIframeMessage } from "@xsbcme/vue-tab-router";

postCurrentIframeMessage({ type: "page:message" });

iframe 页面发送消息

ts
window.parent.postMessage({ type: "iframe:ping" }, window.location.origin);

加载后修改样式

onIframeLoad 会暴露 iframe 元素。跨域时只能操作 iframe 元素本身;同源时可以访问内部 document

ts
createTabsManager({
  views: {
    modules,
  },
  iframe: {
    onLoad({ iframe, tab }) {
      iframe.style.backgroundColor = "#fff";

      try {
        if (tab.viewProps?.iframeDemo && iframe.contentDocument) {
          const style = iframe.contentDocument.createElement("style");
          style.textContent = "body { outline: 4px solid rgba(22, 93, 255, .18); }";
          iframe.contentDocument.head.appendChild(style);
        }
      } catch {
        // 跨域 iframe 不能访问内部 document。
      }
    },
  },
});