Skip to content

五种接入怎么选

五种方式共享同一份契约,行为逐字一致(由 cross-mode-parity 硬红线保证)。 差别不在"能播什么",而在宿主框架隔离级别,以及你能对播放器做多少事


一分钟决策

你有构建环境吗?
├─ 没有(CMS / Markdown / 邮件 / 后台富文本)
│   → ⑤ 静态 iframe URL                  ← 能力最弱,先读下面的警告
└─ 有 → 需要样式 / 脚本隔离吗(第三方页面、多播放器共存、CSS 打架、宿主是 SSR 框架)?
        ├─ 不需要 → React 项目 → ① React inline   ← 能力最全,性能最好
        │           Vue 项目   → ② Vue inline
        └─ 需要   → React 项目 → ③ React iframe
                    Vue 项目   → ④ Vue iframe

先问隔离、再问框架 —— 框架决定的只是包名,隔离决定的才是你能做什么。


能力对比

① React inline② Vue inline③ React iframe④ Vue iframe⑤ 静态 iframe URL
安装@sentinel-lab/video-react@sentinel-lab/video-vue@sentinel-lab/video-react-frame@sentinel-lab/video-vue-frame无需 npm
收事件✅ 全部✅ 全部✅ 全部✅ 全部✅ 全部(需 embed-helper)
下命令(play / pause / seek …)✅ 同步✅ 同步✅ 异步✅ 异步完全不能
配置方式props 完整对象props 完整对象props 完整对象props 完整对象⚠️ 只能 URL query
样式 / 脚本隔离❌ 同 DOM❌ 同 DOM
直接触达 <video> DOM❌ 跨域❌ 跨域❌ 跨域
首帧速度最快最快慢一点(多一次 CDN 加载)慢一点慢一点
自定义覆盖层 UI✅ 最自由✅ 最自由⚠️ 只能在 iframe 外叠⚠️ 只能在 iframe 外叠⚠️ 只能在 iframe 外叠
版本升级跟你的构建走跟你的构建走CDN 路径控制CDN 路径控制CDN 路径控制

「下命令」那行的同步 / 异步是真差异:inline 直连播放内核,pause() 调完就生效; iframe 隔着 postMessage,所有命令返回 Promise。从 iframe 换到 inline 时多余的 await 无害,反过来漏了 await 就会读到旧状态。

换授权源不是一条消费方命令:四种组件接入更新 source 并递增组件 key 重建;静态 iframe URL 替换整个 iframe src。这样才能覆盖跨内核和直播/点播变化,详见鉴权与签名失效


① React inline / ② Vue inline — 能力最全,但没有隔离

选它当:追求首帧速度、要写复杂的自定义覆盖层、要直接摸 <video> 元素。 React 项目用 @sentinel-lab/video-react,Vue 项目用 @sentinel-lab/video-vue —— 两者是镜像关系:同一份 props、同一份事件、同一条数据路径,连产物体积都一样 (实测各 318.6KB gz,差别只在框架桥接那几 KB)。

它的缺点:

  • 没有任何隔离。播放器 DOM 就在你的页面里,你的全局 CSS(尤其是 * { box-sizing }video { width: 100% } 这类)会直接影响它,反之亦然。
  • 打进你的 bundle。虽然框架桥接那层很薄,但 xgplayer 内核本身不小。
  • SSR 宿主要额外处理:播放内核在 Node 里求值会直接报错,Next.js 要 next/dynamic({ ssr: false }),Nuxt / VitePress 这类要异步组件 + 客户端渲染。

不推荐用在

  • 你的页面要嵌到第三方站点里(CSS 环境不可控)
  • 同页要放很多个播放器(每个都是完整实例,内存吃紧)
  • 团队里有人喜欢写全局样式 😅

③ React iframe / ④ Vue iframe — 隔离换一点性能

选它当:需要样式隔离、需要把播放器版本和主应用解耦、宿主是 SSR 框架。

它们的缺点:

  • 多一次网络往返。iframe 要从 CDN 加载 embed-app,首帧比 inline 慢。
  • 摸不到 <video>。跨域 iframe,想做的一切都得走契约里的命令和事件。 契约里没有的,你就做不了(这是刻意的,见 为何没有逃生舱)。
  • 覆盖层 UI 只能叠在 iframe 外面。你没法把一个按钮画进播放器内部的 DOM, 只能在 iframe 上方绝对定位。全屏时要额外处理层级。
  • 依赖 iframe 地址可达。默认使用官方 Cloudflare 地址;受管环境应向部署方取得 已验证的 origin 与精确版本,再写入配置。

不推荐用在

  • 首帧时间是硬指标的场景(比如信息流里的自动播放预览)
  • 完全离线 / 无可用 iframe 地址的环境

Vue 以前为什么没有 inline?现在为什么有了?

从前的理由是「inline 要维护一套 Vue 的覆盖层组件体系,和 React 那套是两份实现、 两份测试、两处漂移源」(ADR-002)。

那个前提已经没了。 覆盖层现在是框架无关的 custom elements,React 和 Vue 用的是同一份,「Vue 版覆盖层」这件事根本不存在。ADR-002 因此转 Superseded (见 ADR-052),Vue inline 是正式支持的接入方式。

仍然选 iframe 的理由是隔离和 SSR,不再是「Vue 只能这样」。


⑤ 静态 iframe URL — 最弱的一种,务必读完

选它当:你根本没有构建环境。CMS 正文、Markdown 文档、邮件模板、后台富文本编辑器。

这是唯一不需要 npm 的接入方式,代价是能力大幅缩水。

html
<iframe
  src="https://sentinel-video.pages.dev/embed/v1.0.25/?src=https%3A%2F%2Fcdn.example.com%2Fa.m3u8&type=hls&muted=1&autoplay=1"
  allow="autoplay; fullscreen; picture-in-picture; encrypted-media; screen-wake-lock"
  allowfullscreen
  style="width:100%;aspect-ratio:16/9;border:0"
></iframe>

⚠️ 它做不到的事

1. 完全没有命令通道。

这是最大的限制。静态场景下宿主和 iframe 没有 Penpal 握手,只有单向的 postMessage 广播。所以:

不能用代码让它 play、pause、seek、切源、调音量。一次都不能。

用户能点原生控件操作,但你的 JS 碰不到它。想要"点我方按钮播放"这种最基础的交互, 必须换成前面四种里的任意一种。

2. 配置只能靠 URL query。

没法传嵌套对象,所以多码率、字幕、自定义 messages 这些都传不了。 能传的就是下面这 12 个参数。

3. URL 里的东西全是明文。

包括签名 URL 的 token。用户 F12 就能看到。(其实所有接入方式都一样, 但静态场景下 URL 直接写在 HTML 正文里,更容易被复制走。)

能收事件,但要装一个包

事件是单向广播的,能收。但要正确校验和分发,建议用 @sentinel-lab/video-embed-helper:

js
import { on } from '@sentinel-lab/video-embed-helper'

const off = on('ended', () => {
  location.href = nextEpisodeUrl
})

注意这里的矛盾

这个包只有 on / off / destroy 三个方法——没有任何命令,因为压根没有命令通道。 而且既然你都能 import 了,说明你其实是有构建环境的,那不如直接用 ①~④。

embed-helper 真正的适用场景很窄:宿主页面有一点点 JS 能力(比如能插 <script>), 但没法引入完整框架。

iframe URL 全部参数(12 个)

参数类型说明
srcstring必填。视频 URL,要 encodeURIComponent
typehls | flv | mp4显式指定格式。签名 URL 建议一定传,否则从后缀猜会猜错
livebool是否直播。影响源优先级(直播 FLV 优先,点播 HLS 优先)
lowLatencybool开 LL-HLS。所有浏览器都必须显式开(ADR-024 + ADR-056)
posterstring封面图 URL
presethomepage-preview预设配置组合(目前只有这一个,见 ADR-032)
autoplaybool自动播放。几乎必须配 muted=1,否则会被浏览器拒
mutedbool静音
loopbool循环播放
controlsbool显示原生控件。静态场景下建议保持开启——你没有命令通道,关了用户就没法操作了
playsinlinebool移动端行内播放(不自动全屏)
interactivebool是否响应用户交互

布尔值写法都一样:1 / true / 空串(如 ?muted)为真,0 / false 为假。

参数非法 = 直接黑屏

URL 是外部输入,一律过 Zod 校验。任何一个参数不合法,整个配置解析失败,播放器不会启动。 这是刻意的——宁可明确失败,也不要拿半残配置创建播放器然后黑屏让你查半天。 上线前务必真机点一遍你拼出来的 URL。


还是拿不定主意?

选择接入入口:React 和 Vue 的组件示例分别在各自的原生门户中运行;静态 iframe 请直接看嵌入指南。