跳转到内容
关系图谱

bilibili:B 站视频嵌入完全指南

1,878 字 7 分钟

属性 1
series
VaultKit 包指南

@vaultkit/vitepress-plugin-bilibili 为 VitePress / markdown-it 提供 ::: bilibili 容器,一行 Markdown 嵌入哔哩哔哩官方外链播放器(player.bilibili.com)。它基于 markdown-it-container 在构建期直接产出 HTML + CSS,客户端零 JS;属性语义逐项对齐官方播放器 QueryString 文档,并针对嵌入场景矫正了最要命的一个默认值——官方缺省会自动播放,本插件缺省显式关闭。

16 个容器属性覆盖 UGC 稿件、OGV 番剧、分 P、起播时间、弹幕、画质、宽高比等全部嵌入场景。本文逐项给出语义与示例,并讲透 deferSrc 模式背后的 bfcache 机理。

为什么需要它

直接从 B 站「分享 → 嵌入代码」复制 iframe 到 Markdown 里,有三个绕不开的问题:

  • 复制来的 <iframe> 定宽定高,不随正文栏宽自适应,移动端直接破版
  • 官方播放器缺省自动播放——读者页面滚到一半突然出声,是嵌入场景的反模式
  • 裸 HTML 混进 Markdown 难以维护,参数拼错也没有任何提示

::: bilibili 容器把这些一次收拾干净:CSS aspect-ratio 自适应宽度、缺省显式 autoplay=0、未知属性与视频 ID 全缺都在构建期抛错(fail-fast),坏嵌入根本到不了线上。

安装

sh
pnpm add -D @vaultkit/vitepress-plugin-bilibili

注册 markdown-it 插件并在主题入口引入一次样式:

ts
// .vitepress/config.ts
import { bilibiliEmbed } from '@vaultkit/vitepress-plugin-bilibili'
import { defineConfig } from 'vitepress'

export default defineConfig({
  markdown: {
    config(md) {
      md.use(bilibiliEmbed)
    },
  },
})
ts
// .vitepress/theme/index.ts
import '@vaultkit/vitepress-plugin-bilibili/styles/index.css'

如果你用的是 @vaultkit/vitepress-theme-vault 主题,本插件开箱即带,::: bilibili 容器无需任何配置即可直接用。

快速上手

语法(花括号 key=value,容器正文渲染为视频下方的说明文字,可省):

md
::: bilibili {bvid="BV1B7411m7LV" danmaku=false}
容器正文渲染为视频下方的说明文字(可省)。
:::

效果——下面就是本页构建期真实渲染出的播放器:

官方文档示例视频 —— 这行文字来自容器正文,渲染为视频下方的 caption。

容器属性逐项详解

全部 16 个属性一览(VitePress 下由内建 markdown-it-attrs 原生解析;非 VitePress 宿主自动回退内置解析,额外接受无引号值):

属性进播放器 URL说明
bvidbvidUGC 视频 BV 号(推荐)
aidaidUGC 视频 av 号
cidcid稿件分 P 弹幕池 ID
episodeIdepisodeIdOGV 番剧单集 ID
seasonIdseasonIdOGV 番剧季 ID
pp多 P 集数
tt起播时间点(秒)
posterposter展示封面
autoplayautoplay自动播放(缺省显式置 0
mutedmuted静音
danmakudanmakufalse 关闭弹幕
highQualityhigh_quality高画质开关
ratio不进 URL容器宽高比(CSS aspect-ratio
title不进 URLiframe 无障碍标题
class不进 URL透传外层容器类名
id不进 URL透传外层容器 id

bvid

UGC 稿件的 BV 号,与 aid 二选一,官方与本插件都推荐 bvid(av 号存在整型溢出史):

md
::: bilibili {bvid="BV1B7411m7LV"}
:::

aid

UGC 稿件的 av 号(纯数字,不带 av 前缀),老文章迁移时常见:

md
::: bilibili {aid=170001}
:::

cid

稿件分 P 的弹幕池 ID,精确锁定某一 P 的弹幕上下文。注意:提供 cid 时官方播放器会忽略 p 参数

md
::: bilibili {bvid="BV1B7411m7LV" cid=279786}
:::

episodeId

OGV(番剧)单集 ID。官方播放器里 episodeId 优先级高于 aid / bvid——同时给出时以番剧集为准。kebab-case 别名 episode-id 同义:

md
::: bilibili {episodeId=374717}
:::

seasonId

OGV 番剧的季 ID,播放该季正片。别名 season-id

md
::: bilibili {seasonId=33802}
:::

p

多 P 稿件的集数(从 1 起)。如上所述,有 cid 时官方忽略此参数:

md
::: bilibili {aid=170001 p=2}
:::

t

起播时间点,单位秒。写教程时「从第 90 秒的重点看起」非常好用:

md
::: bilibili {bvid="BV1B7411m7LV" t=90}
:::

poster

是否展示封面(布尔)。true/1 序列化为 poster=1false/0poster=0,不写则不进 URL、跟随官方缺省:

md
::: bilibili {bvid="BV1B7411m7LV" poster=true}
:::

autoplay

自动播放。这是本插件唯一「强制矫正」的属性:不写时也会显式输出 autoplay=0——官方缺省是自动播放,嵌入场景是反模式。确有需要(如配合 muted 的静音自动播)再显式打开:

md
::: bilibili {bvid="BV1B7411m7LV" autoplay=true muted=true}
:::

muted

静音起播。与 autoplay=true 搭配是浏览器自动播放策略下唯一稳妥的组合:

md
::: bilibili {bvid="BV1B7411m7LV" muted=true}
:::

danmaku

弹幕开关。写 danmaku=false 关闭初始弹幕层,正文嵌入通常建议关掉以免干扰阅读:

md
::: bilibili {bvid="BV1B7411m7LV" danmaku=false}
:::

highQuality

高画质开关,序列化为官方的 high_quality=1。注意官方限制:未登录观众最高仍只有 360p,聊胜于无。别名 high-quality

md
::: bilibili {bvid="BV1B7411m7LV" highQuality=true}
:::

ratio

容器宽高比,直接写进 iframe 的 CSS aspect-ratio,缺省 16 / 9。竖屏视频、4:3 老番都靠它:

md
::: bilibili {bvid="BV1B7411m7LV" ratio="4 / 3"}
:::

title

iframe 的无障碍标题(title 属性),屏幕阅读器朗读用,缺省 Bilibili video player(可经插件 options 改缺省):

md
::: bilibili {bvid="BV1B7411m7LV" title="示例视频:VitePress 入门"}
:::

class

透传到外层 .bili-embed 容器的额外类名,配合站点自定义 CSS 微调:

md
::: bilibili {bvid="BV1B7411m7LV" class="hero-video"}
:::

id

透传到外层容器的 id,可作页内锚点跳转目标:

md
::: bilibili {bvid="BV1B7411m7LV" id="demo-video"}
:::

插件 Options 全览

md.use(bilibiliEmbed, options)BilibiliOptions 共三项:

名称类型默认值说明
defaultTitlestring'Bilibili video player'iframe title 缺省文案
defaultRatiostring'16 / 9'容器宽高比缺省值(CSS aspect-ratio 语法)
deferSrcbooleanfalse输出 data-src 而非 src,由宿主 IntersectionObserver 注入
ts
md.use(bilibiliEmbed, {
  defaultTitle: '哔哩哔哩视频',
  defaultRatio: '16 / 9',
  deferSrc: true,
})

设计机制

deferSrc 与 bfcache:为什么 loading="lazy" 还不够

缺省产物的 iframe 带 loading="lazy",看似万事大吉,实则有两个坑:

  • 浏览器原生懒加载的预载距离过大(Chromium 慢网下可达数千像素),播放器 iframe 往往在读者远未滚到之前就开始加载,抢首屏带宽
  • 导航中的子帧会让页面失去 bfcache 资格——读者点出去再按返回键,本可秒回的页面被迫整页重建

deferSrc: true 把懒加载语义从浏览器手里拿回来:构建产物输出 data-src 而非 src,由宿主用 IntersectionObserver 在 iframe 进入视口附近时才注入真实地址(宿主须自备注入脚本,@vaultkit/vitepress-theme-vault 内置)。

还有一个反直觉的细节:deferSrc 模式下产物刻意不带 loading="lazy"——无 src 的 lazy iframe 会让 Chromium 挂起一个子帧 about:blank 导航(bfcache 排除原因 SubframeIsNavigating),反而破坏 bfcache。懒加载语义已由 IO 注入承担,二者不可叠加。

构建期 fail-fast 校验

两条校验都发生在构建期,坏嵌入不会带病上线:

  • bvid / aid / episodeId 全缺 → 抛「缺少视频 ID」
  • 出现 16 个白名单之外的属性名(比如把 danmaku 拼成 danmu)→ 抛「未知属性」

属性解析双轨

VitePress 内建 markdown-it-attrs,花括号属性由它原生解析进 token.attrs;非 VitePress 的 markdown-it 宿主没有这一步,插件自动回退到解析 info 行尾花括号的内置实现,且与 markdown-it-attrs 行为一致(接受无引号值)。同一份 Markdown 在两类宿主下产物相同。

常见问题

视频区域空白? 检查 bvid 是否有效、视频是否为区域限定内容——播放器加载失败不会回调页面。

能不能不写 caption? 可以。容器正文只含空白时视觉无痕(样式对空 caption 做了收敛处理),单纯 ::: bilibili {...} + ::: 即可。

样式没生效? styles/index.css 需在主题入口显式引入一次,它不随 JS 自动注入(插件 sideEffects: false)。

相关链接