VitePress全局组件封装

前言

VuePress 主题默认有 <CodeGroup> 组件用于切换代码很方便。
如图所示:
image

痛点

使用 VitePress 后,官方没有提供 <CodeGroup> 组件类似的方案。

参考 VuePress 源码

https://github.com/vuejs/vuepress/blob/38e98634af117f83b6a32c8ff42488d91b66f663/packages/%40vuepress/theme-default/global-components/CodeGroup.vue

自己封装 CodeGroup

项目下新建全局组件:components\CodeGroup.vue
实现代码如下:

<template>
  <ClientOnly>
    <div class="theme-code-group">
      <div class="theme-code-group__nav">
        <ul class="theme-code-group__ul">
          <li
            v-for="(tab, i) in codeTabs"
            :key="tab.title"
            class="theme-code-group__li"
          >
            <button
              class="theme-code-group__nav-tab"
              :class="{
                'theme-code-group__nav-tab-active': i === activeCodeTabIndex,
              }"
              @click="changeCodeTab(i)"
            >
              {{ tab.title }}
            </button>
          </li>
        </ul>
      </div>
      <slot />
      <pre v-if="codeTabs.length < 1" class="pre-blank"> // 没代码~ </pre>
    </div>
  </ClientOnly>
</template>

<script>
export default {
  name: 'CodeGroup',
  data() {
    return {
      codeTabs: [],
      activeCodeTabIndex: -1,
    }
  },
  watch: {
    activeCodeTabIndex(index) {
      this.activateCodeTab(index)
    },
  },
  mounted() {
    this.loadTabs()
  },
  methods: {
    changeCodeTab(index) {
      this.activeCodeTabIndex = index
    },
    async loadTabs() {
      await this.$nextTick()
      this.codeTabs = (this.$slots.default() || [])
        .filter(slot => Boolean(slot.props))
        .map((slot, index) => {
          if (slot.props.active === '') {
            this.activeCodeTabIndex = index
          }

          return {
            title: slot.props.title,
            elm: slot.el,
          }
        })

      if (this.activeCodeTabIndex === -1 && this.codeTabs.length > 0) {
        this.activeCodeTabIndex = 0
      }

      this.activateCodeTab(0)
    },
    activateCodeTab(index) {
      this.codeTabs.forEach(tab => {
        if (tab.elm) {
          tab.elm.style.display = 'none'
        }
      })

      if (this.codeTabs[index]?.elm) {
        this.codeTabs[index].elm.style.display = 'block'
      }
    },
  },
}
</script>

<style scoped>
/* .theme-code-group {} */
.theme-code-group__nav {
  margin-bottom: -35px;
  background-color: #282c34;
  padding-bottom: 22px;
  border-radius: 6px;
  padding-left: 10px;
  padding-top: 10px;
}
.theme-code-group__ul {
  margin: auto 0;
  padding-left: 0;
  display: flex;
  list-style: none;
}
.theme-code-group__li {
  margin-top: 0;
  margin-right: 10px;
}
.theme-code-group__nav-tab {
  border: 0;
  padding: 5px;
  cursor: pointer;
  background-color: transparent;
  font-size: 0.85em;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.9);
  font-weight: 600;
}
.theme-code-group__nav-tab-active {
  border-bottom: #42b983 1px solid;
}
.pre-blank {
  color: #42b983;
  margin: 0;
}
</style>

全局注册组件

新建主题配置文件:docs\.vitepress\theme\index.js
VitePress 中注册全局组件:

import DefaultTheme from 'vitepress/theme'
import CodeGroup from '../../../components/CodeGroup.vue'

export default {
  ...DefaultTheme,
  enhanceApp({ app }) {
    app.component('CodeGroup', CodeGroup)
  }
}

在 md 文档中使用

🚨注意:由于语法限制,以下代码统一缩进了一个 Tab 制表符,在使用时请删除 Tab 制表符。

	# CodeGroup 自定义全局组件

	## 测试 JS + TS

	<CodeGroup>
	  <div title="ts" active>

	```ts{2}
	// 注释
	const add = (a: number, b: number): number => {
	  return a + b
	}
	console.log(add(1, 2))
	```

	  </div>
	  <div title="js">

	```js{2}
	// 注释
	const add = (a, b) => {
	  return a + b
	}
	console.log(add(1, 2))
	```

	  </div>
	</CodeGroup>

	## 测试 yarn + npm + pnpm 

	<CodeGroup>
	  <div title="yarn" active>

	```sh
	# install in your project
	yarn add -D vitePress
	```

	  </div>
	  <div title="npm">

	```sh
	# install in your project
	npm install -D vitePress
	```

	  </div>
	  <div title="pnpm">

	```sh
	# install in your project
	pnpm install -D vitePress
	```

	  </div>
	</CodeGroup>

效果预览

image

posted @ 2022-07-19 18:41  MegaSu  阅读(753)  评论(0编辑  收藏  举报