vitepress使用
vitepress 官网
vitepress 中文翻译网站 (中文翻译网站内容是残缺的)
vitepress 版本
1.0.0-alpha.35
首页示例
md
---
layout: home
hero:
name: 管理系统布局组件
text: 适用于上下或左右分栏风格的布局
tagline: 基于vue3, 仅支持现代浏览器,简洁、易扩展、好用
image:
src: /logo.png
alt: 后台系统布局组件
actions:
- theme: brand
text: 开始
link: /articles/02-用法详解/
- theme: alt
text: 获取源代码
link: https://gitee.com/free_pan/backend-layout-wk
features:
- icon: ⚡️
title: 简洁而纯粹,只为布局
- icon: 📦
title: 各布局组件之间逻辑隔离,使扩展变得简便
- icon: 🛠️
title: 除了vue3,再无任何其他依赖
---
右侧文章目录显示多级
PROJECT_ROOT/docs/.vitepress/config.ts
ts
module.exports = {
themeConfig: {
// 配置顶部的文字(不配置则是英文)
outlineTitle: '文章目录',
// 表示显示h2-h6的标题
outline: 'deep',
},
}
顶部导航
数据结构
ts
interface NavItem {
/**
* 文本
*/
text: string
/**
* 跳转链接
* 注意事项:如果要配置动态sidebar,则这个link必须以'/'结尾
*/
link?: string
/**
* 高亮匹配路由
*/
activeMatch?: string
/**
* 子导航
*/
children?: NavItem[]
}
手动配置
PROJECT_ROOT/docs/.vitepress/config.ts
ts
import { UserConfig } from 'vitepress'
module.exports = {
themeConfig: {
nav: [
{ text: '作品集', link: '/articles/作品集/', activeMatch: '/作品集/' },
{
text: '作品集',
link: '/articles/作品集/',
activeMatch: '/作品集/',
children: [
// 省略...
],
},
],
},
} as UserConfig
动态生成
ts
import path from 'path'
import { readdirSync, statSync } from 'fs'
/**
* 判断是否为markdown文件
*
* @param {string} fileName 文件名
*
* @return {[boolean]} 有返回值则表示是markdown文件,否则不是
*/
function isMarkdownFile(fileName: string) {
return fileName.match(/.+\.md$/)
}
interface NavGenerateConfig {
/**
* 是否启用路由匹配显示激活状态. 默认:false
*/
enableDirActiveMatch: boolean
/**
* 需要遍历的目录. 默认:articles
*/
dirName?: string
/**
* 最大遍历层级. 默认:1
*/
maxLevel?: number
}
export function getNavData(navGenerateConfig: NavGenerateConfig) {
const { enableDirActiveMatch, dirName = 'articles', maxLevel = 1 } = navGenerateConfig
const dirFullPath = path.resolve(__dirname, `../${dirName}`)
const result = getNavDataArr(dirFullPath, 1, maxLevel, enableDirActiveMatch)
// console.log('navData')
// console.log(result)
return result
}
interface NavItem {
text: string
link?: string
activeMatch?: string
children?: NavItem[]
}
/**
* 获取顶部导航数据
*
* @param {string} dirFullPath 当前需要遍历的目录绝对路径
* @param {number} level 当前层级
* @param {number[]} maxLevel 允许遍历的最大层级
* @param {boolean} enableActiveMatch 是否启用路由匹配显示激活状态
*
* @return {NavItem[]} 导航数据数组
*/
function getNavDataArr(dirFullPath: string, level: number, maxLevel: number, enableActiveMatch: boolean): NavItem[] {
// 获取所有文件名和目录名
const allDirAndFileNameArr = readdirSync(dirFullPath)
const result: NavItem[] = []
allDirAndFileNameArr.map((fileOrDirName: string, idx: number) => {
const fileOrDirFullPath = path.join(dirFullPath, fileOrDirName)
const stats = statSync(fileOrDirFullPath)
const link = fileOrDirFullPath.split('docs')[1].replace('.md', '').replace(/\\/g, '/')
const text = fileOrDirName.match(/^[0-9]{2}-.+/) ? fileOrDirName.substring(3) : fileOrDirName
if (stats.isDirectory()) {
// 当前为文件夹
const dirData: NavItem = {
text,
link: `${link}/`,
}
if (level !== maxLevel) {
dirData.children = getNavDataArr(fileOrDirFullPath, level + 1, maxLevel, enableActiveMatch)
}
if (enableActiveMatch) {
dirData.activeMatch = link + '/'
}
result.push(dirData)
} else if (isMarkdownFile(fileOrDirName)) {
// 当前为文件
const fileData: NavItem = {
text,
link: link,
}
if (enableActiveMatch) {
fileData.activeMatch = link + '/'
}
result.push(fileData)
}
})
return result
}
侧边导航
数据结构
ts
interface SideBarItem {
/**
*名称
*/
text: string
/**
* 是否显示展开/收缩按钮
*/
collapsible?: boolean
/**
* 默认是否收缩
*/
collapsed?: boolean
/**
* 文章列表
*/
items?: SideBarItem[]
/**
* 跳转链接
*/
link?: string
}
手动配置
PROJECT_ROOT/docs/.vitepress/config.ts
ts
import { UserConfig } from 'vitepress'
module.exports = {
themeConfig: {
/**
* 整体的sidebar是一个对象,这个对象的属性是与顶部导航的link值对应
* 当点击的顶部导航的link与sidebar的某个属性完全匹配时,那么侧边就会显示对应属性的哪个sidebar
*/
sidebar: {
'/articles/01-作品集/': [
{
text: '工具类',
items: [{ text: 'xxx', link: 'xxx' }],
},
{
text: 'css/scss/less',
items: [],
},
{
text: '移动端',
items: [],
},
{
text: '算法',
items: [],
},
{
text: 'node',
items: [],
},
],
},
},
} as UserConfig
动态生成
ts
import path from 'path'
import { readdirSync, statSync } from 'fs'
/**
* 判断是否为markdown文件
*
* @param {string} fileName 文件名
*
* @return {[boolean]} 有返回值则表示是markdown文件,否则不是
*/
function isMarkdownFile(fileName: string) {
return fileName.match(/.+\.md$/)
}
interface SidebarGenerateConfig {
/**
* 需要遍历的目录. 默认:articles
*/
dirName?: string
/**
* 忽略的文件名. 默认: index.md
*/
ignoreFileName?: string
/**
* 忽略的文件夹名称. 默认: ['demo','asserts']
*/
ignoreDirNames?: string[]
}
export function getSidebarData(sidebarGenerateConfig: SidebarGenerateConfig = {}) {
const {
dirName = 'articles',
ignoreFileName = 'index.md',
ignoreDirNames = ['demo', 'asserts'],
} = sidebarGenerateConfig
// 获取目录的绝对路径
const dirFullPath = path.resolve(__dirname, `../${dirName}`)
const allDirAndFileNameArr = readdirSync(dirFullPath)
const obj = {}
allDirAndFileNameArr.map(dirName => {
let subDirFullName = path.join(dirFullPath, dirName)
const property = subDirFullName.split('docs')[1].replace(/\\/g, '/') + '/'
const arr = getSideBarItemTreeData(subDirFullName, 1, 2, ignoreFileName, ignoreDirNames)
obj[property] = arr
})
// console.log('sidebarData')
// console.log(obj)
return obj
}
interface SideBarItem {
text: string
collapsible?: boolean
collapsed?: boolean
items?: SideBarItem[]
link?: string
}
function getSideBarItemTreeData(
dirFullPath: string,
level: number,
maxLevel: number,
ignoreFileName: string,
ignoreDirNames: string[]
): SideBarItem[] {
// 获取所有文件名和目录名
const allDirAndFileNameArr = readdirSync(dirFullPath)
const result: SideBarItem[] = []
allDirAndFileNameArr.map((fileOrDirName: string, idx: number) => {
const fileOrDirFullPath = path.join(dirFullPath, fileOrDirName)
const stats = statSync(fileOrDirFullPath)
if (stats.isDirectory()) {
if (!ignoreDirNames.includes(fileOrDirName)) {
// 当前为文件夹
const dirData: SideBarItem = {
text: fileOrDirName,
collapsed: false,
}
if (level !== maxLevel) {
dirData.items = getSideBarItemTreeData(fileOrDirFullPath, level + 1, maxLevel, ignoreFileName, ignoreDirNames)
}
if (dirData.items) {
dirData.collapsible = true
}
result.push(dirData)
}
} else if (isMarkdownFile(fileOrDirName) && ignoreFileName !== fileOrDirName) {
// 当前为文件
const matchResult = fileOrDirName.match(/(.+)\.md/)
const text = matchResult ? matchResult[1] : fileOrDirName
const fileData: SideBarItem = {
text,
link: fileOrDirFullPath.split('docs')[1].replace('.md', '').replace(/\\/g, '/'),
}
result.push(fileData)
}
})
return result
}
动态生成需要有对应的目录与文件结构
- 所有的文章都必须在
PROJECT_ROOT/docs/articles目录,并放入对应的子目录 PROJECT_ROOT/docs/articles的子目录是一级目录(如:01-作品集),这一级目录会被用来生成顶部导航PROJECT_ROOT/docs/articles/一级目录/index.md, 这是一级目录的首页, 该文件必须存在PROJECT_ROOT/docs/articles/一级目录/二级目录,二级目录用于生成左侧导航PROJECT_ROOT/docs/articles/一级目录/二级目录/xxx.md,xxx.md表示你的具体文章,文章必须放在二级目录下面- 无论是自动生成头部导航还是侧边导航,默认都会自动忽略
index.md以及demo目录和asserts目录

调用动态导航生成代码生成导航数据
ts
import { UserConfig } from 'vitepress'
import { getSidebarData, getNavData } from './navSidebarUtil'
module.exports = {
themeConfig: {
// 扫描目录自动生成顶部导航
nav: getNavData({ enableDirActiveMatch: true }),
// 扫描目录自动生成侧边导航数据
sidebar: getSidebarData(),
},
} as UserConfig
配置代码 demo 插件
用于显示代码运行效果以及源代码
pnpm add @pzy915/vite-plugin-vitepress-demo
PROJECT_ROOT/docs/vite.config.ts
ts
import VitePluginVitepressDemo from '@pzy915/vite-plugin-vitepress-demo'
import { defineConfig } from 'vite'
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
// glob: './**/demo/**/*.{vue,jsx,tsx,js,ts}' 这里表示扫描所有demo目录下的vue,jsx,tsx,js,ts作为demo示例代码
plugins: [vueJsx(), VitePluginVitepressDemo({ glob: './**/demo/**/*.{vue,jsx,tsx,js,ts}' })],
server: {
host: '0.0.0.0',
// open: true,
},
})
PROJECT_ROOT/docs/.vitepress/theme/index.ts
ts
import { Component } from 'vue'
import { type EnhanceAppContext, type Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import { AntdTheme } from '@pzy915/vite-plugin-vitepress-demo/theme'
export default {
...DefaultTheme,
enhanceApp({ app, router, siteData }) {
app.component('Demo', AntdTheme)
},
} as Theme
在 md 中使用 demo 插件
会运行代码
md
<demo src="./demo/menu/TestMenu03.vue" title="" desc=""></demo>
如下这种配置方式,在展之后除了显示TestMenu03.vue的源代码外,还会显示Util.js的源代码,如果还有更多需要显示的源代码,则继续往otherSrcArr中添加即可
md
<demo src="./demo/menu/TestMenu03.vue" :otherSrcArr="['./demo/menu/Util.js']" title="" desc=""></demo>
如果只想展示源代码
md
<demo src="./demo/menu/TestMenu03.vue" raw title="" desc=""></demo>
加入第三方依赖
以加入Element-Plus为例
PROJECT_ROOT/docs/.vitepress/theme/index.ts
ts
import { Component } from 'vue'
import { type EnhanceAppContext, type Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
export default {
...DefaultTheme,
enhanceApp({ app, router, siteData }) {
app.use(ElementPlus)
},
} as Theme