搜索 K
主题
@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。
pnpm add -D @vaultkit/vitepress-plugin-code-playground// .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)
},
},
})// .vitepress/theme/index.ts
import '@vaultkit/vitepress-plugin-code-playground/styles/index.css'使用 @vaultkit/vitepress-theme-vault 主题则开箱即带,两个容器都可直接用。
CodePen 语法:
::: codepen {user="team" slug="PNaGbb"}
容器正文渲染为嵌入下方的说明文字(可省)。
:::实活效果:
StackBlitz 语法:
::: stackblitz {id="vitejs-vite-y8mdxg" file="index.html" view="preview" ctl=true}
:::实活效果(ctl=true 点击才加载,不抢页面带宽):
| 属性 | 类型 | 缺省 | 说明 |
|---|---|---|---|
url | string | — | pen 页面地址,自动解析 user/slug |
user | string | — | pen 作者(与 slug 搭配) |
slug | string | — | pen 标识 |
defaultTab | string | result | 默认打开的标签组合 |
theme | string | — | light / dark / 自定义主题 id |
editable | boolean | — | 访客可编辑 |
preview | boolean | — | 点击运行模式 |
height | number | 400 | iframe 高度(px) |
title | string | CodePen: user/slug | iframe 无障碍标题 |
class | string | — | 外层容器额外类名 |
id | string | — | 外层容器元素 id |
pen 页面地址,接受 codepen.io/<user>/pen/<slug>(details / full 形态也认),构建期用正则解析出 user/slug;解析不出直接抛错:
::: codepen {url="https://codepen.io/team/codepen/pen/PNaGbb"}
:::pen 坐标的拆分写法,与 url 二选一(都给时以 url 解析结果为准):
::: codepen {user="chriscoyier" slug="pMBYbb"}
:::嵌入面板默认打开的标签组合,逗号分隔,如 "html,result"、"css,result"、"js,result";缺省 result 只看效果:
::: codepen {user="team" slug="PNaGbb" defaultTab="html,result"}
:::kebab-case 别名 default-tab 同义。
嵌入主题:light / dark,或你在 CodePen 后台创建的自定义主题 id(序列化为官方的 theme-id 参数):
::: codepen {user="team" slug="PNaGbb" theme="dark"}
:::允许访客直接在嵌入里改代码(作者需 Pro 才对匿名访客生效)。真值序列化为 editable=true:
::: codepen {user="team" slug="PNaGbb" editable=true}
:::点击运行模式:URL 路径从 embed 切到 embed/preview,读者点击后才真正加载运行时。一页嵌多个 pen 时强烈建议开启:
::: codepen {user="team" slug="PNaGbb" preview=true}
:::height 为 iframe 高度(正整数 px,非法值构建期抛 TypeError);title 缺省自动生成 CodePen: user/slug;class / id 透传外层容器:
::: codepen {user="team" slug="PNaGbb" height=300 title="示例 pen" class="wide" id="pen-1"}
:::| 属性 | 类型 | 缺省 | 说明 |
|---|---|---|---|
id | string | — | 项目 id(stackblitz.com/edit/<id>) |
github | string | — | owner/repo[/tree/分支/子目录] 仓库路径 |
file | string | — | 初始打开的文件 |
view | string | 自适应 | editor / preview |
theme | string | — | light / dark |
ctl | boolean | — | 点击才加载 |
hideExplorer | boolean | — | 隐藏文件树 |
hideNavigation | boolean | — | 隐藏预览地址栏 |
hideDevtools | boolean | — | 隐藏控制台面板 |
showSidebar | boolean | — | 展开侧栏 |
initialPath | string | — | 预览初始路由 |
startScript | string | — | 启动用的 package.json 脚本名 |
terminalHeight | number | — | 终端面板高度(%) |
devtoolsHeight | number | — | devtools 面板高度(%) |
height | number | 500 | iframe 高度(px) |
title | string | StackBlitz: id 或 github | iframe 无障碍标题 |
class | string | — | 外层容器额外类名 |
id 指向 stackblitz.com 上的项目;github 直接把 GitHub 仓库(可带 /tree/分支/子目录)作为项目打开,二者必给其一,全缺构建期抛错:
::: stackblitz {id="vitejs-vite-y8mdxg"}
:::
::: stackblitz {github="vitejs/vite/tree/main/packages/create-vite/template-vanilla"}
:::注意:id 在这里被项目标识占用,因此 stackblitz 容器不透传元素 id(这也是它与 codepen 属性表唯一的不对称处)。
file 指定编辑器初始打开的文件;view 在 editor / preview 间二选一(缺省按宽度自适应双栏);theme 定嵌入配色:
::: stackblitz {id="vitejs-vite-y8mdxg" file="index.html" view="preview" theme="dark"}
:::Click-to-load:先渲染封面,读者点击才启动 WebContainers。StackBlitz 项目启动要装依赖、跑 dev server,重嵌入页面务必开启:
::: stackblitz {id="vitejs-vite-y8mdxg" ctl=true}
:::裁掉与文章无关的界面区块,聚焦你想让读者看的部分。布尔属性均接受 kebab-case(hide-explorer 等):
::: stackblitz {id="vitejs-vite-y8mdxg" hideExplorer=true hideNavigation=true hideDevtools=true showSidebar=false}
:::序列化时对齐官方参数大小写口径:hideExplorer / showSidebar 驼峰,hidedevtools 全小写——官方文档就是这么不统一,插件替你记住。
initialPath 指定预览 iframe 的初始路由(如 /about);startScript 指定用 package.json 里哪个脚本启动(如 monorepo 里的 dev:docs):
::: stackblitz {github="vitejs/vite/tree/main/packages/create-vite/template-vue" initialPath="/index.html" startScript="dev"}
:::终端与 devtools 面板的高度占比(0–100):
::: stackblitz {id="vitejs-vite-y8mdxg" terminalHeight=30 devtoolsHeight=40}
:::与 codepen 语义一致:iframe 高度(缺省 500 px)、无障碍标题、外层类名:
::: stackblitz {id="vitejs-vite-y8mdxg" height=600 title="Vite 起步模板" class="tall"}
:::codePlayground(md, options) 的 options 形如 { codepen, stackblitz },两个子对象即 codepenEmbed / stackblitzEmbed 的独立选项:
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
codepen.defaultHeight | number | 400 | CodePen iframe 高度缺省值(px) |
codepen.defaultTab | string | 'result' | default-tab 缺省值 |
codepen.deferSrc | boolean | false | 输出 data-src,宿主 IO 注入 |
stackblitz.defaultHeight | number | 500 | StackBlitz iframe 高度缺省值(px) |
stackblitz.deferSrc | boolean | false | 同上 |
md.use(codePlayground, {
codepen: { defaultTab: 'js,result', deferSrc: true },
stackblitz: { defaultHeight: 560, deferSrc: true },
})CodePen 与 StackBlitz 的嵌入本质相同:构造带参 URL → 产出懒加载 iframe → 底部 caption 槽。差异全部收敛在「属性白名单 + URL 序列化」两张表里,包装层(openWrapper / deferSrcAttrs / 布尔序列化 / 高度校验)完全复用。也因此二者可以按需单独注册(codepenEmbed / stackblitzEmbed),不用谁就不注册谁。
与 bilibili 容器 同款机理:原生 loading="lazy" 预载距离过大 + 导航中的子帧破坏 bfcache 资格,deferSrc 改输出 data-src 交给宿主 IntersectionObserver 注入,且刻意不带 loading="lazy"(无 src 的 lazy iframe 会让 Chromium 挂起 about:blank 子帧导航,反而失去 bfcache)。演练场 iframe 是全页面最重的资源,deferSrc 在这里收益最大。
三类错误全部止步构建期:未知属性名、缺 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 渲染,居中小字显示在嵌入下方。