前端演示 1.0.0-rc.127 +
约 3317 字大约 11 分钟
2025-01-08
旧的前端代码演示 已弃用,请迁移至此新的方案。
旧的方案由 vuepress-plugin-md-enhance 提供,感谢在过去 提供的代码演示的支持,在 vuepress-plugin-md-enhance
中代码演示功能也将迁移至 vuepress/ecosystem, 详情请查看 vuepress/ecosystem#293 。
为什么要重新设计?
前端代码演示是一个很有用的功能,但是在旧的方案中,所实现的功能与实际使用场景预期不符。
旧的方案中,比如 vue-demo
仅能支持一些简单的 vue 组件演示,且不能直接导入项目中的依赖,仅能通过 加载外部脚本支持更多功能,且并没提供对 vue sfc
的完全支持,仅能进行简单的代码演示。
而且对脚本代码的编译是在浏览器运行时,先从 CDN 请求加载 babel
,完成后再通过 babel
进行转换, 这需要额外的等待时间完成,同时对于企业内部的项目,在内网环境中无法请求外部资源,导致演示无法正常展示。
在新的方案中,所有的演示代码均是在 nodejs 运行时进行编译转换,因此在浏览器运行时可直接展示演示代码,无需额外的等待时间。 且得益于 nodejs 强大的能力,可以完全支持 vue sfc
的完整功能,且可以直接导入项目中的依赖,让你的演示更加丰富。 更符合实际的使用场景。
概述
此功能支持在 页面中 嵌入 代码演示 功能。前端代码演示由两个主要区域组成: 渲染区 和 代码区 。
其中,渲染区 用于展示代码的执行结果,包括 UI 渲染和 交互;代码区 用于展示源代码,默认是折叠的。
主题提供了 三种不同的 前端代码演示支持:
- vue 组件演示: 支持
vue
组件的演示,像编写一个vue
组件一样编写你的演示代码,可以用于演示如 组件库、composables-api
等外部依赖。 - markdown 演示:支持
markdown
的演示。 - 普通代码演示 :支持原生的
HTML
+JS/TS
+CSS/Less/Sass/Stylus
的代码演示,像编写一个网页一样编写你的演示代码。
主题还提供了 两种不同的使用方式编写演示代码:
嵌入演示代码文件:
@[demo type](url)
可以通过简单的嵌入语法,从文件中导入演示代码。
demo 容器内联演示代码:
::: demo type ``` [lang] code ``` :::
直接在 markdown 文件中编写演示代码,使用
demo
容器包裹即可。
配置
前端代码演示 由 vuepress-plugin-md-power 提供支持。
前端 代码演示 默认不启用,你可以通过配置来启用它。
export default defineUserConfig({
theme: plumeTheme({
plugins: {
markdownPower: {
demo: true, // 启用新的代码演示功能
},
markdownEnhance: {
demo: false, // 禁用旧的代码演示功能
}
}
})
})
语言支持
代码演示支持以下 语言:
- javascript
- typescript
- html
- css
- less
- sass
- stylus
对于 css 预处理语言,你需要在项目中安装对应的预处理器,如 less
、 sass
、 stylus
等。
嵌入语法
不同的代码演示均使用相同的嵌入语法,你可以快速掌握它们的使用方法。
<!-- 语法 -->
@[demo](url)
@[demo [type]](url)
@[demo [type] title="" desc="" expanded code-setting=""](url)
@[demo](url)
是一个固定的语法格式。
[type]
表示类型,支持 三个不同的值:
normal
: 普通代码演示类型。当不传入[type]
参数时,默认为normal
类型。vue
: vue 组件演示类型。markdown
: markdown 演示类型。
url
表示演示代码文件的路径,可以是相对路径或绝对路径,
- 相对路径,以
./
或../
开头,表示相对于当前的 markdown 文件路径。 - 绝对路径,以
/
开头,表示从 vuepress 源目录路径 开始。
<!-- 普通代码演示 -->
@[demo](./demo/normal.html)
@[demo normal](./demo/normal.html)
@[demo](/.vuepress/demo/normal.html)
<!-- vue 组件演示 -->
@[demo vue](./demo/Counter.vue)
@[demo vue](./demo/Counter.ts)
@[demo](/.vuepress/demo/Counter.vue)
<!-- markdown 演示 -->
@[demo markdown](./demo/example.md)
@[demo markdown](/.vuepress/demo/example.md)
其它额外参数:
title="xxx"
:演示标题desc="xxx"
:演示描述expanded
:展开代码区域code-setting="xxx"
:代码设置,值将被拼接在``` [lang]
之后,用于给代码块添加配置。code-setting=":lines-number"
,则会在代码块后面添加:lines-number
,使代码块支持显示行号。code-setting=":collapsed-lines=10"
,则会在代码块后面添加:collapsed-lines=10
,使代码块从第 210行开始折叠。
@[demo vue expanded title="标题" desc="描述" code-setting=":collapsed-lines=10"](./demo/Counter.vue)
demo 容器内联演示
demo 容器内联演示 使用 demo
容器包裹演示代码,可以在 markdown 文件中快速地编写演示代码,如下:
::: demo [type] title="" desc="" expanded
<!-- 代码块 -->
:::
所有参数与 @[demo](url)
语法相同。
<!-- 普通代码演示 -->
::: demo
```html
<!-- html 代码 -->
```
``` js
// js 代码
```
``` css
/* css 代码 */
```
:::
<!-- vue 组件演示 -->
::: demo vue
``` vue
<!-- vue 代码 -->
```
:::
<!-- markdown 演示 -->
::: demo markdown
``` md
<!-- markdown 代码 -->
```
:::
还可以在 ::: demo
容器中 使用 ::: code-tabs
容器包裹代码块,以获得更好的交互效果。
:::: demo
::: code-tabs
@tab HTML
```html
<!-- html 代码 -->
```
@tab javascript
``` js
// js 代码
```
@tab css
``` css
/* css 代码 */
```
::::
当期望使用 Typescript 或 Less/Sass/Stylus
时,通过修改 ``` [lang]
的值即可:
:::: demo
::: code-tabs
@tab HTML
```html
<!-- html 代码 -->
```
@tab Typescript
``` ts
// ts 代码
```
@tab Scss
``` scss
/* scss 代码 */
```
::::
vue 组件演示
vue 组件演示 是一个很强大的功能,对于演示代码不做任何限制,这甚至完全取决于 bundler
对于 vue 的支持。 你还可以直接在演示代码中导入项目中安装的依赖,就像你在写一个 vue 项目的组件一样。
因此,你可以直接使用它来为 你的组件库 提供演示示例,或者为你的 composables-api
提供演示示例。
嵌入语法
你可以直接使用以下方式在页面中嵌入一个 vue 组件演示:
输入:
@[demo vue title="计数器" desc="点击 +1 按钮,计数器自增 1"](./demo/Counter.vue)
查看 ./demo/Counter.vue
代码
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div class="counter">
<p>
计数器:{{ count }}
</p>
<button type="button" class="btn" @click="count += 1">
+ 1
</button>
</div>
</template>
<style scoped>
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
</style>
输出:
计数器:0
计数器
点击 +1 按钮,计数器自增 1
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div class="counter">
<p>
计数器:{{ count }}
</p>
<button type="button" class="btn" @click="count += 1">
+ 1
</button>
</div>
</template>
<style scoped>
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
</style>
也可以嵌入一个 .ts
编写的 vue 组件:
输入:
@[demo vue title="计数器" desc="点击 +1 按钮,计数器自增 1"](./demo/Counter.ts)
查看 ./demo/Counter.ts
代码
import { defineComponent, h, ref } from 'vue'
import styles from './Counter.module.css'
export default defineComponent({
setup() {
const count = ref(0)
return () => h('div', {
class: 'counter',
}, [
h('p', `计数器:${count.value}`),
h('button', {
type: 'button',
class: styles.btn,
onClick: () => count.value += 1,
}, '+ 1'),
])
},
})
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
输出:
计数器:0
计数器
点击 +1 按钮,计数器自增 1
import { defineComponent, h, ref } from 'vue'
import styles from './Counter.module.css'
export default defineComponent({
setup() {
const count = ref(0)
return () => h('div', {
class: 'counter',
}, [
h('p', `计数器:${count.value}`),
h('button', {
type: 'button',
class: styles.btn,
onClick: () => count.value += 1,
}, '+ 1'),
])
},
})
对于 .js/.ts
编写的组件,请使用 css module
来编写样式以实现样式隔离
可以在演示代码中导入外部依赖, 以导入 @vueuse/core
中的 useToggle()
为例:
输入:
@[demo vue title="useToggle" desc="useToggle() 演示"](./demo/Toggle.vue)
./demo/Toggle.vue
<script setup lang="ts">
import { useToggle } from '@vueuse/core'
const [value, toggle] = useToggle()
</script>
<template>
<div>
<p>Value: {{ value ? 'ON' : 'OFF' }}</p>
<div style="display: flex;gap: 16px;">
<button class="btn" @click="toggle()">
Toggle
</button>
<button class="btn" @click="value = true">
Set On
</button>
<button class="btn" @click="value = false">
Set Off
</button>
</div>
</div>
</template>
<style scoped>
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
</style>
输出:
Value: OFF
useToggle
useToggle() 演示
<script setup lang="ts">
import { useToggle } from '@vueuse/core'
const [value, toggle] = useToggle()
</script>
<template>
<div>
<p>Value: {{ value ? 'ON' : 'OFF' }}</p>
<div style="display: flex;gap: 16px;">
<button class="btn" @click="toggle()">
Toggle
</button>
<button class="btn" @click="value = true">
Set On
</button>
<button class="btn" @click="value = false">
Set Off
</button>
</div>
</div>
</template>
<style scoped>
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
</style>
容器语法
在 markdown 文件中使用 demo
容器包裹演示代码,可以快速地编写演示代码,如下:
输入:
展开查看完整代码
::: demo vue title="计数器" desc="点击 +1 按钮,计数器自增 1"
```vue
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div class="counter">
<p>计数器:{{ count }}</p>
<button type="button" class="btn" @click="count += 1">
+ 1
</button>
</div>
</template>
<style scoped>
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
</style>
```
:::
输出:
计数器:0
计数器
点击 +1 按钮,计数器自增 1
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div class="counter">
<p>计数器:{{ count }}</p>
<button type="button" class="btn" @click="count += 1">
+ 1
</button>
</div>
</template>
<style scoped>
.btn {
padding: 0 8px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
</style>
注意
vue demo 容器语法虽然也支持 使用 .js/ts + css
的方式来嵌入演示代码, 但主题不推荐这样做。因为 样式无法被隔离,这可能导致样式污染。
参考示例
:::: demo vue title="标题" desc="描述"
::: code-tabs
@tab Counter.ts
```ts
import { defineComponent, ref } from 'vue'
export default defineComponent({
// code
})
```
@tab Counter.css
```css
/* css code */
```
:::
::::
普通代码演示
普通代码演示支持 html
、 css/less/sass/stylus
、 js/ts
语言。
适合于相对简单的代码演示,比如 一个样式渲染效果,一个交互效果,一个功能 等。
普通代码演示还支持跳转到 codePen
和 jsFiddle
中查看。
同时,也支持通过 外部链接 的方式引入 第三方的库,比如 jQuery
, dayjs
等。
document
的差异
普通代码演示 的代码 运行在 ShadowDOM
中,从而实现与 站点其他内容的隔离。避免对环境的污染。
因此,在普通演示的脚本代码中,全局对象 document
指向的是 ShadowDOM
,请着重注意此差异。 如果您需要使用 浏览器的全局对象,请使用 window.document
代替 document
。
如果引入了如 JQuery
库,由于此差异,$(selector)
的行为会发生变化,要查询 ShadowDOM
中的元素, 需要使用 $(selector, document)
,即在第二个参数中传入 document
作为查询的上下文。
不建议过于复杂的演示。
嵌入语法
使用嵌入语法时,对于导入的 代码演示文件,使用 .html
作为文件后缀。在 .html
文件中, 你可以像编写一个 HTML 页面一样编写 演示代码:
<!-- html 代码 -->
<div id="app">
演示内容
<div>
<!-- 脚本内容,使用 lang 属性设置语言, 默认为 js -->
<script lang="ts">
</script>
<!-- 样式内容,使用 lang 属性设置语言, 默认为 css -->
<style lang="css">
</style>
<!-- 可选的配置文件 json 格式 -->
<script type="config">
{
"jsLib": [],
"cssLib": []
}
</script>
每一个区域的内容都是可选的。但请注意,不支持存在多个相同的区域。区域的顺序无要求。 除了 <script>
和 <style>
之外的内容,都被认为是 HTML 代码。
你可以在 <script type="config"></script>
内使用 json
格式声明要加载的其他依赖资源。
比如,加载 jQuery
, 以及 normalize.css
:
<div>xxxx</div>
<script type="config">
{
"jsLib": [
"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"
],
"cssLib": [
"https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css"
]
}
</script>
一个常规的示例:
输入:
@[demo title="示例" desc="这是一个常规演示"](./demo/normal.html)
查看 ./demo/normal.html
代码
<div id="app">
<h3>vuepress-theme-plume</h3>
</div>
<script lang="ts">
const a = 'So Awesome!'
const app = document.querySelector('#app')
app.appendChild(window.document.createElement('small')).textContent = a
</script>
<style lang="css">
#app {
font-size: 2em;
text-align: center;
}
</style>
输出:
示例
这是一个常规演示
<div id="app">
<h3>vuepress-theme-plume</h3>
</div>
const a = 'So Awesome!'
const app = document.querySelector('#app')
app.appendChild(window.document.createElement('small')).textContent = a
#app {
font-size: 2em;
text-align: center;
}
引入 jQuery
, dayjs
和 normalize.css
的示例:
输入:
@[demo title="示例" desc="这是一个常规演示"](./demo/normal-lib.html)
查看 ./demo/normal-lib.html
代码
<div id="app">
<h3>vuepress-theme-plume</h3>
<p id="message"></p>
<datetime id="datetime"></datetime>
</div>
<script>
$('#message', document).text('So Awesome!')
const datetime = $('#datetime', document)
setInterval(() => {
datetime.text(dayjs().format('YYYY-MM-DD HH:mm:ss'))
}, 1000)
</script>
<style lang="css">
#app {
font-size: 2em;
text-align: center;
}
</style>
<script type="config">
{
"jsLib": [
"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
"https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js"
],
"cssLib": ["https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css"]
}
</script>
输出:
示例
这是一个常规演示
<div id="app">
<h3>vuepress-theme-plume</h3>
<p id="message"></p>
<datetime id="datetime"></datetime>
</div>
$('#message', document).text('So Awesome!')
const datetime = $('#datetime', document)
setInterval(() => {
datetime.text(dayjs().format('YYYY-MM-DD HH:mm:ss'))
}, 1000)
#app {
font-size: 2em;
text-align: center;
}
容器语法
在 markdown 文件中使用 demo 容器包裹演示代码,可以快速地编写演示代码,如下:
展开查看完整示例代码
::: demo title="示例" desc="描述" expanded
```json
{
"jsLib": [],
"cssLib": []
}
```
```html
<!-- html 代码 -->
```
```js
// js 代码
```
```css
/* css 代码 */
```
:::
```
还可以在 ::: demo
中包裹 ::: code-tabs
以获得更好的代码块展示效果:
展开查看完整示例代码
:::: demo title="示例" desc="描述" expanded
```json
{
"jsLib": [],
"cssLib": []
}
```
::: code-tabs
@tab HTML
```html
<!-- html 代码 -->
```
@tab Javascript
```js
// js 代码
```
@tab CSS
```css
/* css 代码 */
```
:::
::::
```
一个常规的 容器示例:
输入:
展开查看完整示例代码
:::: demo title="常规示例" desc="一个常规示例"
::: code-tabs
@tab HTML
```html
<div id="app">
<h3>vuepress-theme-plume</h3>
</div>
```
@tab Javascript
```js
const a = 'So Awesome!'
const app = document.querySelector('#app')
app.appendChild(window.document.createElement('small')).textContent = a
```
@tab CSS
```css
#app {
font-size: 2em;
text-align: center;
}
```
:::
::::
输出:
常规示例
一个常规示例
<div id="app">
<h3>vuepress-theme-plume</h3>
</div>
const a = 'So Awesome!'
const app = document.querySelector('#app')
app.appendChild(window.document.createElement('small')).textContent = a
#app {
font-size: 2em;
text-align: center;
}
引入 jQuery , dayjs 和 normalize.css 的示例:
输入:
展开查看完整示例代码
:::: demo title="常规示例" desc="一个常规示例"
```json
{
"jsLib": [
"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
"https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js"
],
"cssLib": ["https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css"]
}
```
::: code-tabs
@tab HTML
```html
<div id="app">
<h3>vuepress-theme-plume</h3>
<p id="message"></p>
<datetime id="datetime"></datetime>
</div>
```
@tab Javascript
```js
$('#message', document).text('So Awesome!')
const datetime = $('#datetime', document)
setInterval(() => {
datetime.text(dayjs().format('YYYY-MM-DD HH:mm:ss'))
}, 1000)
```
@tab CSS
```css
#app {
font-size: 2em;
text-align: center;
}
```
:::
::::
输出:
常规示例
一个常规示例
<div id="app">
<h3>vuepress-theme-plume</h3>
<p id="message"></p>
<datetime id="datetime"></datetime>
</div>
$('#message', document).text('So Awesome!')
const datetime = $('#datetime', document)
setInterval(() => {
datetime.text(dayjs().format('YYYY-MM-DD HH:mm:ss'))
}, 1000)
#app {
font-size: 2em;
text-align: center;
}
Markdown 演示
在页面中演示 markdown 源代码 和渲染结果。
嵌入语法
输入:
@[demo markdown title="公告板" desc="公告板代码示例"](/.vuepress/bulletin.md)
展开查看 /.vuepress/bulletin.md
代码
::: center
**QQ 交流群:** [792882761](https://qm.qq.com/q/FbPPoOIscE)
![QQ qr_code](/images/qq_qrcode.png){width="618" height="616" style="width: 200px"}
您在使用过程中遇到任何问题,欢迎通过 [issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new/choose) 反馈。也欢迎加入我们的 QQ 交流群一起讨论。
:::
输出:
公告板
公告板代码示例
::: center
**QQ 交流群:** [792882761](https://qm.qq.com/q/FbPPoOIscE)
![QQ qr_code](/images/qq_qrcode.png){width="618" height="616" style="width: 200px"}
您在使用过程中遇到任何问题,欢迎通过 [issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new/choose) 反馈。也欢迎加入我们的 QQ 交流群一起讨论。
:::
容器语法
输入:
展开查看完整代码
:::: demo markdown title="公告板" desc="公告板代码示例"
```md
::: center
**QQ 交流群:** [792882761](https://qm.qq.com/q/FbPPoOIscE)
![QQ qr_code](/images/qq_qrcode.png){width="618" height="616" style="width: 200px"}
您在使用过程中遇到任何问题,欢迎通过 [issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new/choose) 反馈。也欢迎加入我们的 QQ 交流群一起讨论。
:::
```
::::
输出:
公告板
公告板代码示例
::: center
**QQ 交流群:** [792882761](https://qm.qq.com/q/FbPPoOIscE)
![QQ qr_code](/images/qq_qrcode.png){width="618" height="616" style="width: 200px"}
您在使用过程中遇到任何问题,欢迎通过 [issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new/choose) 反馈。也欢迎加入我们的 QQ 交流群一起讨论。
:::