仿element风格Demo展示
大约 3 分钟
仿 element 风格 Demo 展示
最近使用 vuepress-theme-hope 搭建博客时,发现之前使用的 demo 展示出现了问题,控制台报错并且不展示组件了,赶紧去官网看了下,也是同样的问题,这里决定自己封装下
安装 markdown 增强插件
先安装用于增强 markdown 的插件
pnpm install markdown-it-container markdown-it创建插件
在根目录下创建的 plugins 存放本地插件
这里仿照 vuepress2 生态系统中的 markdown 增强,通过插件检索 :::demo :::,重新渲染成组件 PreviewComponents
// plugins/markdown/plugin-preview-demo/index.ts
import { Plugin } from 'vuepress';
import mdContainer from 'markdown-it-container';
export const previewDemoPlugin = (options = {}): Plugin => {
  options = Object.assign({});
  return (app) => {
    return {
      name: 'vuepress-plugin-preview-demo',
      extendsMarkdown: (md) => {
        md.use(mdContainer, 'demo', {
          validate(params) {
            return params.trim().match(/^demo\s*(.*)$/);
          },
          render(tokens, idx) {
            if (tokens[idx].nesting === 1) {
              return `
                <PreviewComponents>
              `;
            }
            return `</PreviewComponents>`;
          },
        });
      },
    };
  };
};编写预览组件
预览插件主要分两块
- 组件渲染区域
- 代码展示区域
这里组件渲染通过具名插槽动态插入,代码通过默认插槽渲染代码块,这样可以省去代码块高亮处理
预览组件样式则参考 element-plus 文档样式
<template>
  <div class="example">
    <div class="example-showcase">
      <slot name="component"></slot>
    </div>
    <el-divider />
    <div class="op-btns">
      <el-tooltip
        class="box-item"
        content="复制代码"
        effect="dark"
        placement="bottom"
      >
        <i class="el-icon" style="font-size: 16px" @click="handleCopy">
          <CopyCode />
        </i>
      </el-tooltip>
      <el-tooltip
        class="box-item"
        :content="expandedTooltip"
        effect="dark"
        placement="bottom"
      >
        <i class="el-icon" style="font-size: 16px" @click="handleExpanded">
          <ViewCode />
        </i>
      </el-tooltip>
    </div>
    <el-collapse-transition>
      <div class="example-source-wrapper" v-show="isExpanded" ref="code">
        <slot />
      </div>
    </el-collapse-transition>
    <transition name="el-fade-in">
      <div
        class="example-float-control"
        v-show="isExpanded"
        @click="handleHidden"
      >
        <el-icon><CaretTop /></el-icon>
        <span>隐藏源代码</span>
      </div>
    </transition>
  </div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { CaretTop } from '@element-plus/icons-vue';
import { CopyCode, ViewCode } from '@icons';
import { copyText } from '@utils';
const isExpanded = ref(false);
const expandedTooltip = computed(() => {
  return isExpanded.value ? '隐藏源代码' : '查看源代码';
});
const code = ref<any>(null);
const handleCopy = () => {
  const text = (code.value as HTMLElement).innerText;
  copyText(text);
};
const handleExpanded = () => {
  isExpanded.value = !isExpanded.value;
};
const handleHidden = () => {
  isExpanded.value = false;
};
//
</script>
<style lang="scss" scoped>
.example {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  .el-divider {
    margin: 0;
  }
  .op-btns {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    height: 24px;
    padding: 8px;
    .el-icon {
      margin: 0 8px;
      cursor: pointer;
      color: #909399;
    }
  }
}
.example-showcase {
  padding: 24px;
  background: #fff;
}
.example-source-wrapper {
  margin: -15px 0;
  min-height: 30px;
}
.example-float-control {
  position: sticky;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  border-top: 1px solid #dcdcdc;
  height: 44px;
  background-color: #fff;
  font-size: 14px;
  color: #909399;
  cursor: pointer;
  &:hover {
    color: #409eff;
  }
  span {
    margin-left: 10px;
  }
}
</style>将组件注册
// src/.vuepress/client.ts
// 引入Demo预览组件,于本地插件中动态替换 ::: demo ::: 标签
import PreviewComponents from './components/PreviewComponents/index.vue';
export default defineClientConfig({
  enhance({ app }) {
    // ...
    app.component('PreviewComponents', PreviewComponents);
  },
});