@deot/docs-playground

中文文档
1.2.11发布于 2026/11/02MITTypeScriptES Module近 30 天下载 2023

@deot/docs-playground 是基于 Vue REPL 的浏览器代码预览组件,支持单文件或多文件运行、文件浏览与编辑、响应式视口、代码高亮和父页面导航事件。

原始使用文档
文档目录11 节
  1. 安装
  2. 快速开始
  3. Playground 属性
  4. 事件与双向绑定
  5. 预览内的临时高度
  6. 其他公共导出
  7. 运行环境
  8. 运行时模块加载
  9. 仓库内 examples
  10. 仓库内验证
  11. 许可证

@deot/docs-playground

@deot/docs-playground 是基于 Vue REPL 的浏览器代码预览组件,支持单文件或多文件运行、文件浏览与编辑、响应式视口、代码高亮和父页面导航事件。

安装

pnpm add @deot/docs-playground @deot/docs-locale @deot/vc vue

快速开始

<template>
    <Playground
        v-model:files="files"
        v-model:entry="entry"
        v-model:viewport="viewport"
        :views="['runtime', 'files']"
        :viewport-options="['auto', 375, [375, 667]]"
        :locale="zhCN"
    />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Playground } from '@deot/docs-playground';
import { zhCN } from '@deot/docs-locale';
import '@deot/docs-playground/dist/index.style.css';

const entry = ref('App.vue');
const viewport = ref<'auto' | number | [number, number]>('auto');
const files = ref({
    'App.vue': `<template><h1>Hello Playground</h1></template>`
});
</script>

宿主应用还需要加载 @deot/vc-components 的组件样式。本仓库示例使用 /node_modules/@deot/vc-components/dist/index.style.css。

Playground 属性

属性类型默认值说明
modelValuestring''单文件模式的入口 SFC 内容。
filesRecord<string, string>{}多文件源码映射。非空时优先于 modelValue。
entrystring''入口文件;未设置时使用文件映射的第一项。
views('runtime' | 'files')[]['runtime']可用视图及初始视图顺序。
viewport'auto' | number | [number, number]'auto'运行时宽度,或固定宽高。
viewportOptionsPlaygroundViewport[]['auto', 375]可切换的视口列表。
stylelessbooleanfalse只渲染无工具栏的运行时预览。
titlestring''顶栏标题;空串或不传不渲染标题文本。runtime / 双视图始终保留顶栏;files-only 仅在有标题时显示顶栏。带标题时生成锚点(# 链接),规则对齐 markdown-it-anchor。
idstring''标题锚点 id;未传时从 title 自动生成。仅挂在内联 runtime 标题上,弹窗不重复。
expandabletrue | numberundefined开启预览高度展开;未传不显示控件;true 展开到剩余视口;正数为目标高度(px)。
previewScrollerbooleanfalse在 iframe 内用 @deot/vc Scroller 替换原生滚动条;开启后会动态探测 Scroller,不存在则回退原生滚动。
localbooleantrue在当前文档编译并挂载。业务模块通过运行时 ESM 加载,可通过 import map 逐个覆盖;预览使用所选 Vue 运行时挂载。
optionsPlaygroundOptions{}传给 Vue REPL store 的实例级选项;cdnURL 会同时作用于预览样式和默认 import map。
previewOptionsSandboxProps['previewOptions']undefined传给当前 iframe 的 preview 选项。
localeLanguageen-US界面语言;未传入时使用上层 Locale Provider。

事件与双向绑定

事件说明
update:modelValue入口文件内容更新。
update:files文件映射更新。
update:entry入口文件更新。
update:viewport视口更新。
change当前入口内容被编辑。
navigateiframe 内的 <DocsLink to="..."> 请求父页面导航。

Playground 只接受来自自身 iframe 的导航消息。宿主应用应监听 navigate 并交给自己的 Router 处理。

预览内的临时高度

Playground 会在预览应用中注入 docs:playground,也可通过 iframe 内的 window.$docsPlayground 访问同一服务。适用于 ActionSheet、Modal、Drawer、Message 等依赖视口高度的示例,无需自行撑高 DOM 或轮询 window.innerHeight。

import { inject, ref } from 'vue';
import type { PlaygroundService } from '@deot/docs-playground';

const playground = inject<PlaygroundService>('docs:playground')!;
const visible = ref(false);

// 定义时不执行,点击后先等待高度就绪,再打开弹层;visible 关闭时恢复。
// visible 是 ref 且省略 handler 时,默认执行 visible.value = true。
const handleOpen = playground.run(560, { visible });

// 也可生成 boolean 状态处理函数:true 扩展高度,false 恢复高度。
const handleVisibleChange = playground.run(560);

// 支持 getter 和 computed;不能传入普通 boolean 快照。
const openDrawer = playground.run(560, { visible: () => state.drawerVisible }, () => {
    state.drawerVisible = true;
});

// 回调返回的 Promise 表示弹层关闭时,可省略 options。
const openActionSheet = playground.run(560, async () => {
    await MActionSheet.open({ title: '请选择操作' });
});

run(height)、run(height, handler) 和 run(height, options, handler) 均返回事件处理函数。传入 handler 时会透传调用参数和回调结果,最后一个参数始终为 handler。options 当前只包含 visible?: Ref<boolean> | (() => boolean);传入可写 Ref<boolean> 时可以省略 handler,默认将其设为 true。getter 和只读 computed 需要显式 handler。示例中的 state、MActionSheet 由业务代码提供。

接口行为
enable(height): Promise<boolean>设置手动临时高度,等待 iframe 实际高度达到要求后返回 true。连续调用替换旧的手动请求。
disable(): void释放手动请求;重复调用无副作用,不影响 run 的请求。
run(height)返回 boolean 事件处理函数;收到 true 时调用 enable(height),收到 false 时调用 disable()。
run(height, handler)等待高度后执行;同步返回或 Promise 完成时恢复高度,异常也恢复并继续抛出。
run(height, { visible: ref })等待高度后默认执行 visible.value = true,并在状态关闭时恢复高度。
run(height, { visible }, handler)执行前监听状态,关闭时恢复;回调完成后未打开或回调抛错时也释放。异步打开流程应由回调返回的 Promise 覆盖。

同一个 run 返回函数在回调与弹层均结束前会复用当前 Promise,避免重复点击打开;不同函数的请求独立管理并取最大高度。Message / Toast 等立即返回的 API 应通过 visible 或关闭回调恢复高度,不能把函数返回当成弹层关闭。

const handleMessage = async () => {
    if (!await playground.enable(240)) return;
    try {
        Message.info({ content: '已保存', onClose: () => playground.disable() });
    } catch (error) {
        playground.disable();
        throw error;
    }
};

高度为正有限 CSS px,表示 iframe 内部可视高度,不包含 previewInset 与边框。普通预览取原有高度与临时请求的最大值,释放后恢复当前视口规则;独立窗口受可用屏幕空间限制。等待超时(2 秒)、取消或空间不足时返回 false;run 在这些情况下不执行回调并返回 undefined。

在 setup 中创建的 run 会随当前 Vue scope 自动取消等待、移除监听并释放高度;其他位置创建时绑定预览应用。手动 enable/disable 绑定整个预览应用,子组件单独卸载时按需自行 disable()。刷新、重新运行及应用卸载会统一释放请求。服务不取消已经启动的业务任务,也不调用组件的 destroy();示例保留必要的业务 onUnmounted,只需删除高度等待使用的 disposed 标记。

类型导出为 PlaygroundService、PlaygroundRunOptions 和 PlaygroundWindow。Window 类型可通过 (window as PlaygroundWindow).$docsPlayground 显式使用;服务仅存在于 Playground 预览应用,不向普通页面声明必然存在的全局变量。接入项目需使用包含该接口的新版 Playground。

其他公共导出

导出说明
CodePreview带语言标识、复制按钮和高亮的只读代码预览。
Editor基于 @deot/vc Portal 的共享文件编辑器入口。
highlightCode(code, filename)按文件名识别语言并返回高亮 HTML。
highlightCodeByLanguage(code, language)按显式语言高亮。
resolveHighlightLanguage(filename)将常见扩展名映射为 highlight.js 语言。
registerVueHighlight(api?)向 highlight.js 注册 Vue SFC 语法。
vueHighlightVue SFC 的 highlight.js 语言定义。
slugifyPlaygroundTitle(title) / resolvePlaygroundTitleId(title, id?)标题锚点 id,规则对齐 markdown-it-anchor。
DEFAULT_CDN_URL默认 CDN 根地址(jsDelivr)。
createBuiltinImports(cdnURL?) / createBuiltinStyles(cdnURL?)按 CDN 生成内置 import map 与预览样式表 URL。
resolveCdnURL(url, cdnURL?, config?)内置转换后调用可选 resolve.cdn;默认读取当前站点配置。
normalizeCdnURL(value?)规范化 CDN 根地址。

同时导出 PlaygroundFiles、PlaygroundView、PlaygroundViewport、PlaygroundOptions、PlaygroundPreviewOptions、EditorFilesChange 和 EditorFilesChangeAction 类型。

站点级 Playground 模块与预览 CSS 默认由 Client 的 $docs.modules 与 $docs.styles 管理(见 @deot/docs-client)。模块地址优先级由低到高为:内置默认 → $docs.modules → 管理页覆盖 → 实例 options.builtinImportMap.imports。实例只覆盖声明的模块,其余继承默认配置;也可通过 options.cdnURL 调整默认 CDN。

modules 未配置时,实际导入的模块使用内置映射或按 npm 包名补齐 CDN ESM 地址;local 模式默认复用宿主的 Vue 和 vue-router。开发与生产的配置规则如下:

  • 在 development 条件内将真实 npm 包名指向本地源码:开发使用本地模块;生产不执行该分支,使用内置映射或默认 CDN ESM 地址,前提是公共 CDN 能提供该模块。
  • 自定义别名、私有模块等无法由公共 CDN 按导入名解析的模块:需要显式提供生产可访问的 ESM 地址,不会根据开发地址推导线上地址或真实包名。
  • 不加 development 判断,直接配置本地路径:生产仍使用同一路径,不会自动转换成 CDN 地址。/__docs/module/<包名> 仅由 doc dev 提供,preview 和 build 不提供该地址。

以上规则适用于 local: true 与 local: false;显式模块地址加载失败会直接报错,不会回退 CDN。配置示例与行为对照见 Client 配置文档。模块地址补齐不会自动加载对应包的自定义 CSS。

预览 CSS 按「内置样式表 → $docs.styles → 管理页覆盖」合并,同名 key 覆盖,最终 URL 去重。local: false 加载到预览 iframe;local: true 加载到宿主页面,会影响宿主及其他示例。local 实例共享相同 URL 的 stylesheet,最后一个使用者卸载时清理自身创建的链接,保留宿主已有链接;示例编译产生的 CSS 单独注入和清理。

local 模式下,Vue 文件内所有 <style> 块(含 SCSS / Sass)统一按 scoped 编译,展示和编辑的源码保持原样。每次 Vue 文件编译生成独立的 scopeId,同名或相同源码的示例也互不共用作用域。:deep()、:slotted() 和 :global() 保留 Vue 原有语义;独立导入的 CSS / SCSS / Sass 文件仍按全局样式处理。local: false 保留源码声明的样式作用域。

内置样式表为 @deot/style/dist/index.normalize-only.css、@deot/vc-components/dist/index.style.css 和 @deot/style/dist/index.css。开发与生产的配置规则如下:

  • 在 development 条件内覆盖内置样式表:开发使用本地 CSS;生产不执行该分支,自动使用内置 CDN,无须另配。
  • 在 development 条件内添加非内置的自定义 CSS:开发使用本地 CSS;生产需要显式配置可访问的地址,否则不会加载。不会根据模块导入自动推断自定义 CSS。
  • 不加 development 判断,直接配置本地路径:生产仍使用同一路径,不会自动转换成线上地址;需要按环境配置地址,或保证生产也能访问该路径。

配置示例见 $docs.styles 的开发与生产配置。管理页已保存的 CSS 覆盖优先于站点默认配置。

运行环境

  • iframe 运行时通过公共 CDN 加载 Vue 和默认 import map 中的依赖;local 运行时默认复用宿主 Vue 和 vue-router,业务包通过 ESM 加载,需要网络访问或配置可访问的本地 ESM 地址。
  • iframe 模式初始化时会补齐宿主页缺失的 globalThis.process 和 process.env,兼容 @vue/repl 的 JSX/TSX 编译;已有对象及环境值保持不变,不模拟其他 Node API。
  • 默认 CDN 为 https://cdn.jsdelivr.net/npm;未使用 launch 时可通过 options.cdnURL 调整默认节点。launch 已选点时统一使用 $docs.runtime.cdn,优先于实例 cdnURL。
  • jsDelivr npm、旧 unpkg.com 和 esm.unpkg.com 地址在实际加载时转换,再经过可选 $docs.resolve.cdn({ url })。jsDelivr 转换入口使用 /+esm,ESM UNPKG 使用包或文件入口。预览 CSS、import map、裸包回退和 Sass 共用该规则;原始映射与缓存不被改写。
  • 直接渲染时,未配置的 npm 模块(含作用域包、版本和子路径)自动通过上述 ESM 规则加载,无需预先登记包名。组件包的传递依赖也适用,预览文件和远程模块中的 import() 在调用时加载。可用 options.builtinImportMap.imports 指定依赖的版本和 URL;显式 URL 加载失败会直接报错。
  • Vue SFC 的 <style lang="scss"> / lang="sass" 以及独立 .scss / .sass 文件会在浏览器里编译;_partial.scss 只作为 @use 依赖。
  • local 模式按模块识别显式 URL 覆盖,即使地址与内置默认相同也会加载该 URL。组件包和工具包统一经运行时加载器读取 ESM,不复用宿主的业务包副本。远程模块的 Vue 导入统一绑定到所选运行时;覆盖 vue 时,编译产物、挂载、组件引用、注入和 vue-router 使用对应的远程运行时。不同预览的覆盖互不影响,各预览独立执行远程模块。
  • local 模式在当前页面按 URL 缓存外部模块的下载和转换成果,并合并并发请求;切换文档时可复用,最多保留最近使用的 128 个模块。各预览仍独立执行模块并解析依赖覆盖;失败的加载不缓存,同源模块实时读取,刷新页面会清空缓存。
  • 每个 Playground 实例都使用自己的 preview 配置和 iframe 消息来源校验。
  • local 默认开启:同一份 files 在当前文档里编译。直接渲染不撑开预览高度,expandable 固定为关闭,docs:playground.run 不会申请临时高度;<DocsLink> 仍通过 docs:navigate 通知。local: false 改回 iframe Sandbox。

运行时模块加载

加载器按模块说明符解析依赖,不识别组件名,不维护可选依赖白名单。静态导入在挂载前准备,动态导入在实际调用时准备;组件库内部的可选依赖沿用组件自身的加载时机。因此普通示例不会因加载组件包而预先下载所有动态依赖,也无需给组件额外增加异步包装。

显式模块地址优先于默认 CDN。CDN 返回的带版本依赖地址会保留版本;同名显式覆盖可以替换该依赖。失败后可再次调用动态导入或刷新预览重试,错误包含请求地址与原因;模块自身的执行异常继续报错。

依赖须能作为浏览器 ESM 执行。若 CDN 转换产物无法正常运行,可通过现有模块配置指定包的原始 ESM 文件,无需增加包名适配。加载器不猜测 npm 包对应的全局变量名,也不向 window 写入依赖;若库自身优先读取已有全局实现,该行为由库保留。已在宿主页其他位置执行的原生 import() 不受接管,iframe Sandbox 继续使用自己的 import map 机制。

仓库内 examples

在 monorepo 根目录执行 npm run dev 可预览 packages/playground/examples 下的单文件与多文件 Playground 示例。

仓库内验证

在仓库根目录执行:

npm run test -- --package-name playground
npm run build -- --package-name playground

许可证

MIT

版本与发布记录

@deot/docs-playground 共发布 30 个版本,首次发布于 2026/08/20;过去 12 个月发布了 30 个版本。

版本发布日期距今
1.2.11latest2026/11/0212 天前
1.2.102026/11/0212 天前
1.2.92026/10/3015 天前
1.2.82026/10/2718 天前
1.2.72026/10/2025 天前
1.2.62026/10/2025 天前

关于 @deot/docs-playground 的常见问题

@deot/docs-playground 是什么?

@deot/docs-playground 是基于 Vue REPL 的浏览器代码预览组件,支持单文件或多文件运行、文件浏览与编辑、响应式视口、代码高亮和父页面导航事件。

如何安装 @deot/docs-playground?

在项目目录运行 npm install @deot/docs-playground,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:vue(^3.5.39)、@deot/vc(^1.2.6)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@deot/docs-playground 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@deot/docs-playground 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@deot/docs-playground 可以商用吗?

@deot/docs-playground 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@deot/docs-playground 的下载量有多少?

根据 npm 官方下载统计,@deot/docs-playground 在 2026/10/12 – 2026/11/10 这 30 天内被下载 2,023 次,日均约 67 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@deot/docs-playground 还在维护吗?

最新版本 1.2.11 发布于 2026/11/02(12 天前);至今共发布 30 个版本,其中过去 12 个月发布了 30 个;首次发布于 2026/08/20。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。