Files
ArtPlayer/packages/artplayer-vitepress/docs/tool/iframe.md

7.7 KiB
Raw Permalink Blame History

Iframe 通信工具

English

在父页面控制 iframe 内的播放器,并把子页面事件传回父页面。它是独立构造器,不是放进 ArtPlayer plugins 数组的插件。本页描述当前未发布分支;父子页面都需要自行部署。

安装与双端接入

yarn add artplayer-tool-iframe

父页面使用默认导入 import ArtplayerToolIframe from 'artplayer-tool-iframe'。script 使用 dist/artplayer-tool-iframe.js,全局名为 ArtplayerToolIframe。

子页面也必须加载此工具并主动调用 inject;如果需要创建播放器,还要在子页面加载 ArtPlayer 和提供容器。例如仓库的 /iframe.html 使用:

<div class="artplayer-app" style="width:100%;height:100%"></div>
<script src="./uncompiled/artplayer/index.js"></script>
<script src="./uncompiled/artplayer-tool-iframe/index.js"></script>
<script>ArtplayerToolIframe.inject();</script>

以上路径属于本站开发输出,部署应用时替换成实际构建路径。下面保留父页面原始示例,它创建 iframe 并指向 /iframe.html:

▶ Run Code
// npm i artplayer-tool-iframe
// import ArtplayerToolIframe from 'artplayer-tool-iframe';

const $iframe = document.createElement('iframe')
$iframe.allowFullscreen = true
$iframe.width = '100%'
$iframe.height = '100%'

const $container = document.querySelector('.artplayer-app')
$container.innerHTML = ''
$container.appendChild($iframe)

const iframe = new ArtplayerToolIframe({
  iframe: $iframe,
  url: '/iframe.html',
})

window.addEventListener('artplayer:example:cleanup', () => {
  iframe.destroy()
  $iframe.remove()
}, { once: true })

iframe.message(({ type, data }) => {
  switch (type) {
    case 'fullscreenWeb':
      if (data) {
        $iframe.classList.add('fullscreenWeb')
      }
      else {
        $iframe.classList.remove('fullscreenWeb')
      }
      break
    default:
      break
  }
})

iframe.commit(() => {
  const art = new Artplayer({
    container: '.artplayer-app',
    url: '/assets/sample/video.mp4',
    fullscreen: true,
    fullscreenWeb: true,
  })

  art.on('fullscreenWeb', (state) => {
    ArtplayerToolIframe.postMessage({
      type: 'fullscreenWeb',
      data: state,
    })
  })
}).catch((error) => {
  if (!iframe.destroyed)
    console.error(error)
})

父页面实例

构造参数为 { iframe: HTMLIFrameElement, url: string },两项必填。构造时设置 iframe.src 并注册消息监听器;不会自动安装子页面脚本。iframe 元素由调用方创建和移除。

成员 行为
commit(callback) 提取函数体字符串,在子页面执行;返回响应 Promise
postMessage({ type, data, id? }) 向子页面发送请求并等待响应;发送时分配自己的数值 id,忽略传入 id
message(callback) 设置一个消息通知回调,后一次覆盖前一次;同步返回 void
onMessage(event) 父页面接收器;通常由工具监听原生 message 事件
destroy() 同步、幂等;移除本实例监听和导航观察,拒绝已发送及等待中的请求
url / $iframe 初始配置地址及 iframe 元素;修改 url 字段本身不等于导航
injected / destroyed 注入及销毁状态
promises 待处理请求对象;历史回调字段拼写为 resove 和 reject
messageCallback 当前通知回调;默认函数,运行时也可为 null

通知只接收 { type, data },this 为该工具实例,不带 id 或私有文档标记。匹配请求 id 的非 error 消息会正常结算;error 响应转换为 Error 拒绝。自定义消息类型仍可使用,但子页面需实现对应响应;未响应的请求没有默认超时。

注入前请求每 200ms 检查一次,不在 inject 事件里同步清空队列。数值 id 是关联编号,不保证等于 Date.now。销毁时未完成请求以 The instance has been destroyed 拒绝;调用方应处理 Promise 失败。

commit 的执行方式

普通函数体可 return 可结构化克隆的结果。异步结果使用历史的字面量 resolve(...) 约定:

const title = await iframe.commit(() => {
  return document.title; // 在子页面读取
});
const answer = await iframe.commit((resolve) => {
  setTimeout(() => resolve(42), 100);
});

函数体通过字符串截取和 new Function 执行,不传递闭包、父页面局部变量、函数参数或 import。保留花括号;不要使用表达式箭头函数、async 函数体、顶层 await,或重命名异步 resolve 参数。该约定使用文本匹配,并非完整 JavaScript 语法分析。返回值需适合 postMessage,Promise 对象本身不能当作普通返回值发送。执行错误会发送 error 响应,子页面接收器也会拒绝。

子页面静态方法与信任边界

成员 行为
ArtplayerToolIframe.iframe 只读运行时 getter:当前页面是否位于 iframe 内
inject() 发送注入通知并安装子页面接收器;重复调用保留单份监听
postMessage({ type, data, id? }) 向 parent 发送通知/响应,默认 id 为 0,返回 void
onMessage(event) 实际为 async 接收器;处理 commit,普通自定义类型需应用处理

后三项只能在 iframe 中使用;顶层调用抛错或拒绝。两端检查所选窗口与基本消息形状,继续使用 wildcard targetOrigin,未将初始 URL 的 origin 固定为白名单。父子页面内容必须可信;commit 会执行代码,需要允许对应执行方式的 CSP,它不是隔离沙箱或消息数据验证器。

导航、清理和兼容类型

双方升级后会协商文档标记,用于拒绝旧文档响应和取消确认离开页面的请求;标记不是认证凭证。替换 src 暂停发送,确认跨文档离开后取消旧请求;匹配的同文档 hash 变化保留请求。新页面排队请求与旧页面请求区分处理。旧子页面缺少文档标记时,同地址 reload 或内部导航的识别有局限,不能视为已验证 BFCache 恢复。

destroy 不移除 iframe、不销毁其播放器,也不新增静态子页面 destroy API。父页面可在释放工具后自行移除 iframe。独立页面的播放器事件、全屏权限及设备行为需要实际集成测试。

根和 /legacy 保留旧类声明,包括必填 Message.data、只读实例字段、同步静态 onMessage 和 commit 的 ReturnType 推导。准确运行时视图从同一入口按类型导入;没有 /runtime 子路径:

import Iframe from 'artplayer-tool-iframe';
import type { ResolverInstance, RuntimeConstructor } from 'artplayer-tool-iframe';

function connectFrame(element: HTMLIFrameElement) {
  const Runtime = Iframe as RuntimeConstructor;
  const tool = new Runtime({ iframe: element, url: '/iframe.html' }) as ResolverInstance;
  tool.message(function (message) { console.log(this.url, message.type, message.data); });
  const answer = tool.commit<number>((resolve) => { resolve(42); });
  return { tool, answer };
}

公开类型包括 Option、Message、Callbacks、Notification、MessageCallback、OutboundMessage、ProtocolMessage、Resolve、ResolverCallback、RuntimeInstance、ResolverInstance、RuntimeConstructor。类型视图不验证响应数据,也不改变执行协议。现代 CommonJS TS 可用 import Iframe = require('artplayer-tool-iframe');ESM 使用默认导入。构造器没有 .default 自引用。旧 artplayer-plugin-iframe 的包名、命名空间和独立 helper 不等同于本工具,不能仅换依赖名就认为所有入口兼容。