需求
给 code 部分加一个 preview 按键,点击查看代码演示,如下:
- 关闭状态
- 打开状态
在我的博客项目中实现这个操作还算比较简单,因为我的博客是用 vue 写的。使用这个 unplugin-vue-markdown
插件就可以直接用 md 文件当 vue 组件,并且也能直接使用 vue 组件。
首先,定义一个 playground
的 vue 组件,一个默认的插槽接收我们的 md 内容,一个具名插槽接收演示代码
<script setup lang="ts">
const show = ref(false);
</script>
<template>
<div>
<div inline-block cursor-pointer @click="show = !show" title="查看演示">
<div i-material-symbols-light:preview />
<span text-sm select-none>Preview</span>
</div>
<div class="grid gap-2" :class="[show ? 'grid-cols-2' : 'grid-cols-1']">
<div>
<slot />
</div>
<div v-show="show" class="my2 p5 rounded bg-[#fafafa] dark:bg-[#0e0e0e]">
<slot name="preview" />
</div>
</div>
</div>
</template>
然后我们在 md 文件中直接使用即可:
这里我们不用担心 md内容
的样式问题,由于 unplugin-vue-markdown
插件对 md 文件的解析是在 vue 组件之前的,是先去解析了 md 文件再解析内部的 vue 组件。所以 css 这部分代码在传入 playground
组件时已经变成了 html
代码,然后再传入了默认插槽中。然后我们的 preview
直接写 html 就好了。还有一个问题,就是我们的内容 css 样式,如何把这个样式注入实际的页面中呢。
嗯...这里我直接写在 preview
的 html 里了。由于我用了 unocss
,这样最简单哈哈哈哈。
<playground>
```css
.container {
width: 200px;
height: 200px;
background-color: red;
}
@layer {
.container {
background-color: blue;
}
}
```
<template #preview>
<div class="container bg-red w-50px h-50px"></div>
</template>
</playground>
其实我也尝试过在解析 md 时注入 css 样式,unplugin-vue-markdown
提供了一个属性 transforms
,可以拿到解析前后的 md 内容。
我想有两种方式,一是在 transforms.before
里拿到 css 解析为 unocss
的 class 注入 div.container
;二是直接将 css 放在 style 标签里注入 html。
可能写在 markdownItSetup
里会更好,因为这个属性是专门用来设置插件的,不过我在尝试的时候是直接在 transforms.before
里拿到 md 文件的全部内容进行解析注入的,确实是可行的。但感觉不是太好,会影响全局样式;其实好像也能解决,给class添加一个特殊的 id 应该就好了,尝试了一下太麻烦了,最后还是放弃了。
解析为 unocss
的 class 我没有尝试(也是我不知道unocss有没有提供native-css
转unocss
的接口),感觉上这样应该更好一点,肯定不会影响全局样式了,最终都会被unocss解析。
import Markdown from "unplugin-vue-markdown/vite";
export default defineConfig({
plugins: [
Markdown({
transforms: {
before(code) {
// code 解析前的 md 内容
return code;
},
after(code) {
// code 解析后的 md 内容,即 html
return code;
},
},
async markdownItSetup(md){
// 使用插件,比如Shiki
md.use(await MarkdownItShiki())
}
}),
],
});
当然,我这里只是 css 的演示,如果想做 js 的演示呢,要注入 js 吗,想了想,我现在的环境不就可以直接在 md 文件里写 js 吗,哈哈哈哈。直接把md文件当vue组件写就行了,把code传给默认插槽,演示代码传给preview插槽。如下:
<script setup>
import {ref} from "vue"
const count = ref(0);
</script>
<playground :show-default="true">
```vue
<script setup>
const count = ref(0);
</script>
<template>
<div>{{ count }}</div>
<button @click="count++">add</button>
</template>
```
<template #preview>
<div>count: {{ count }}</div>
<button class="bg-gray-200 rounded px2 hover:bg-gray-400" @click="count++">add</button>
</template>
</playground>
就可以直接实现 add count
这样的展示效果了
当然,我这里只是简单的演示,想实现更复杂的功能的话肯定不行,不过展示对我来说够用了。 可以直接到我的博客原文进行查看展示详情。