跳转到内容
关系图谱

code-playground:CodePen 与 StackBlitz 嵌入完全指南

1,830 字 7 分钟

属性 1
series
VaultKit 包指南

@vaultkit/vitepress-plugin-code-playground 一个包提供两个容器:::: codepen::: stackblitz,分别对齐 CodePen Embed Builder 与 StackBlitz Embed URL options 官方文档,在 Markdown 里一行嵌入在线代码演练场。两家同域抽象共包——共享属性解析、deferSrc、caption 槽与一份 CSS。

与系列其他容器一致:markdown-it-container 构建期直接产出 HTML + CSS,客户端零 JS,iframe loading="lazy" 懒加载,未知属性、缺失标识、非法高度全部构建期抛错。本文覆盖 codepen 与 stackblitz 的全部容器属性、全部插件 options,并给出实活 demo。

为什么需要它

技术文章里「可交互的代码示例」比静态代码块的说服力高一个量级:读者能改、能跑、能看到实时结果。CodePen 擅长前端三件套小样,StackBlitz 能跑完整 npm 工程(WebContainers)。但两家官方给的嵌入代码都是裸 HTML + 客户端脚本,混进 Markdown 难维护、参数写错静默失败。

本插件把两家的嵌入参数收编为容器属性,写错任何一个属性名,构建直接失败而不是线上出一块坏 iframe。

安装

sh
pnpm add -D @vaultkit/vitepress-plugin-code-playground
ts
// .vitepress/config.ts
import { codePlayground } from '@vaultkit/vitepress-plugin-code-playground'
import { defineConfig } from 'vitepress'

export default defineConfig({
  markdown: {
    config(md) {
      md.use(codePlayground) // 或分别 md.use(codepenEmbed) / md.use(stackblitzEmbed)
    },
  },
})
ts
// .vitepress/theme/index.ts
import '@vaultkit/vitepress-plugin-code-playground/styles/index.css'

使用 @vaultkit/vitepress-theme-vault 主题则开箱即带,两个容器都可直接用。

快速上手

CodePen 语法:

md
::: codepen {user="team" slug="PNaGbb"}
容器正文渲染为嵌入下方的说明文字(可省)。
:::

实活效果:

CodePen 官方示例 pen —— 这行文字来自容器正文。

StackBlitz 语法:

md
::: stackblitz {id="vitejs-vite-y8mdxg" file="index.html" view="preview" ctl=true}
:::

实活效果(ctl=true 点击才加载,不抢页面带宽):

codepen 容器属性逐项详解

属性类型缺省说明
urlstringpen 页面地址,自动解析 user/slug
userstringpen 作者(与 slug 搭配)
slugstringpen 标识
defaultTabstringresult默认打开的标签组合
themestringlight / dark / 自定义主题 id
editableboolean访客可编辑
previewboolean点击运行模式
heightnumber400iframe 高度(px)
titlestringCodePen: user/slugiframe 无障碍标题
classstring外层容器额外类名
idstring外层容器元素 id

url

pen 页面地址,接受 codepen.io/<user>/pen/<slug>details / full 形态也认),构建期用正则解析出 user/slug;解析不出直接抛错:

md
::: codepen {url="https://codepen.io/team/codepen/pen/PNaGbb"}
:::

user 与 slug

pen 坐标的拆分写法,与 url 二选一(都给时以 url 解析结果为准):

md
::: codepen {user="chriscoyier" slug="pMBYbb"}
:::

defaultTab

嵌入面板默认打开的标签组合,逗号分隔,如 "html,result""css,result""js,result";缺省 result 只看效果:

md
::: codepen {user="team" slug="PNaGbb" defaultTab="html,result"}
:::

kebab-case 别名 default-tab 同义。

theme

嵌入主题:light / dark,或你在 CodePen 后台创建的自定义主题 id(序列化为官方的 theme-id 参数):

md
::: codepen {user="team" slug="PNaGbb" theme="dark"}
:::

editable

允许访客直接在嵌入里改代码(作者需 Pro 才对匿名访客生效)。真值序列化为 editable=true

md
::: codepen {user="team" slug="PNaGbb" editable=true}
:::

preview

点击运行模式:URL 路径从 embed 切到 embed/preview,读者点击后才真正加载运行时。一页嵌多个 pen 时强烈建议开启

md
::: codepen {user="team" slug="PNaGbb" preview=true}
:::

height、title、class、id

height 为 iframe 高度(正整数 px,非法值构建期抛 TypeError);title 缺省自动生成 CodePen: user/slugclass / id 透传外层容器:

md
::: codepen {user="team" slug="PNaGbb" height=300 title="示例 pen" class="wide" id="pen-1"}
:::

stackblitz 容器属性逐项详解

属性类型缺省说明
idstring项目 id(stackblitz.com/edit/<id>
githubstringowner/repo[/tree/分支/子目录] 仓库路径
filestring初始打开的文件
viewstring自适应editor / preview
themestringlight / dark
ctlboolean点击才加载
hideExplorerboolean隐藏文件树
hideNavigationboolean隐藏预览地址栏
hideDevtoolsboolean隐藏控制台面板
showSidebarboolean展开侧栏
initialPathstring预览初始路由
startScriptstring启动用的 package.json 脚本名
terminalHeightnumber终端面板高度(%)
devtoolsHeightnumberdevtools 面板高度(%)
heightnumber500iframe 高度(px)
titlestringStackBlitz: id 或 githubiframe 无障碍标题
classstring外层容器额外类名

id 与 github:两种项目来源

id 指向 stackblitz.com 上的项目;github 直接把 GitHub 仓库(可带 /tree/分支/子目录)作为项目打开,二者必给其一,全缺构建期抛错:

md
::: stackblitz {id="vitejs-vite-y8mdxg"}
:::

::: stackblitz {github="vitejs/vite/tree/main/packages/create-vite/template-vanilla"}
:::

注意:id 在这里被项目标识占用,因此 stackblitz 容器不透传元素 id(这也是它与 codepen 属性表唯一的不对称处)。

file、view、theme

file 指定编辑器初始打开的文件;vieweditor / preview 间二选一(缺省按宽度自适应双栏);theme 定嵌入配色:

md
::: stackblitz {id="vitejs-vite-y8mdxg" file="index.html" view="preview" theme="dark"}
:::

ctl

Click-to-load:先渲染封面,读者点击才启动 WebContainers。StackBlitz 项目启动要装依赖、跑 dev server,重嵌入页面务必开启

md
::: stackblitz {id="vitejs-vite-y8mdxg" ctl=true}
:::

界面开关四件套:hideExplorer / hideNavigation / hideDevtools / showSidebar

裁掉与文章无关的界面区块,聚焦你想让读者看的部分。布尔属性均接受 kebab-case(hide-explorer 等):

md
::: stackblitz {id="vitejs-vite-y8mdxg" hideExplorer=true hideNavigation=true hideDevtools=true showSidebar=false}
:::

序列化时对齐官方参数大小写口径:hideExplorer / showSidebar 驼峰,hidedevtools 全小写——官方文档就是这么不统一,插件替你记住。

initialPath 与 startScript

initialPath 指定预览 iframe 的初始路由(如 /about);startScript 指定用 package.json 里哪个脚本启动(如 monorepo 里的 dev:docs):

md
::: stackblitz {github="vitejs/vite/tree/main/packages/create-vite/template-vue" initialPath="/index.html" startScript="dev"}
:::

terminalHeight 与 devtoolsHeight

终端与 devtools 面板的高度占比(0–100):

md
::: stackblitz {id="vitejs-vite-y8mdxg" terminalHeight=30 devtoolsHeight=40}
:::

height、title、class

与 codepen 语义一致:iframe 高度(缺省 500 px)、无障碍标题、外层类名:

md
::: stackblitz {id="vitejs-vite-y8mdxg" height=600 title="Vite 起步模板" class="tall"}
:::

插件 Options 全览

codePlayground(md, options) 的 options 形如 { codepen, stackblitz },两个子对象即 codepenEmbed / stackblitzEmbed 的独立选项:

名称类型默认值说明
codepen.defaultHeightnumber400CodePen iframe 高度缺省值(px)
codepen.defaultTabstring'result'default-tab 缺省值
codepen.deferSrcbooleanfalse输出 data-src,宿主 IO 注入
stackblitz.defaultHeightnumber500StackBlitz iframe 高度缺省值(px)
stackblitz.deferSrcbooleanfalse同上
ts
md.use(codePlayground, {
  codepen: { defaultTab: 'js,result', deferSrc: true },
  stackblitz: { defaultHeight: 560, deferSrc: true },
})

设计机制

同域抽象共包

CodePen 与 StackBlitz 的嵌入本质相同:构造带参 URL → 产出懒加载 iframe → 底部 caption 槽。差异全部收敛在「属性白名单 + URL 序列化」两张表里,包装层(openWrapper / deferSrcAttrs / 布尔序列化 / 高度校验)完全复用。也因此二者可以按需单独注册(codepenEmbed / stackblitzEmbed),不用谁就不注册谁。

deferSrc 与 bfcache

bilibili 容器 同款机理:原生 loading="lazy" 预载距离过大 + 导航中的子帧破坏 bfcache 资格,deferSrc 改输出 data-src 交给宿主 IntersectionObserver 注入,且刻意不带 loading="lazy"(无 src 的 lazy iframe 会让 Chromium 挂起 about:blank 子帧导航,反而失去 bfcache)。演练场 iframe 是全页面最重的资源,deferSrc 在这里收益最大。

构建期 fail-fast

三类错误全部止步构建期:未知属性名、缺 pen / 项目标识(codepen 缺 url 且缺 user+slug;stackblitz 缺 id 且缺 github)、height 非正整数。宁可构建红,不给读者一块坏 iframe。

常见问题

一页嵌了五六个演练场,页面很卡? CodePen 开 preview=true、StackBlitz 开 ctl=true,都变成点击才加载;再配合插件级 deferSrc 双保险。

StackBlitz 嵌入一直转圈? WebContainers 需要较新浏览器且部分公司网络屏蔽其 CDN;github 形态首次启动要装依赖,耐心或换 id 形态的预构建项目。

caption 支持 Markdown 吗? 支持,容器正文按普通 Markdown 渲染,居中小字显示在嵌入下方。

相关链接