Skip to content

VitePress用本地文件生成目录

方案选择

VitePress并没有提供像sidebar一样直接的全部文件归档导航块,但是有许多方式可以实现这一功能。

  • 手写,最直接方便,鉴于大多数的个人站容量,甚至也是工作量最小的方式,唯一的缺点就是会丧失作为程序员的尊严。
  • 动态生成路由,将文档的时间信息挂载在路由上,然后通过VitePress提供的运行时API,useRouter获取全部文件信息并展示。
  • 利用VitePress提供的构建时数据加载生成数据并展示。

除此之外VitePress还提供了丰富的构建钩子,选择很多,本次选择上述方案最后一种。

数据加载文件

新建js文件(如docs/.vitepress/theme/md.data.js)

js
import { createContentLoader } from 'vitepress'

export default createContentLoader('/**/*.md', //根目录及根目录下级目录的所有md文件
    {
        //包含原始markdown源,我的md博文最后都有成文时间;
        //也可用fs.statSync读取文件mtime;
        includeSrc: true,
        transform(rawData) {
            return rawData
                //加入时间属性,以便排序 
                .map(e => {
                    return {
                        date: e.src.slice(-10),//获取md末尾的成文时间
                        url: e.url,
                    }
                })
                //检查日期是否有效
                .filter(e => !Number.isNaN(new Date(e.date).getTime()))
                .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
        },
    })

在页面中引用数据

md
<script setup>
    import { data } from '../.vitepress/theme/md.data.js'
    //data即为md.data.js中transform返回的结果
    //业务代码。。。
</script>

md文件中混用markdown与vue语法

VitePress允许在md文件中使用vue语法,但是所有的vue标签也都必须顶格书写,从markdown切换到vue语法还需要额外空行,格式丑陋,而且<h>标签无法自动生成目录,所以不推荐混用,建议使用组件。

组件

vue
<recordVue />
<script setup>
import recordVue from '../.vitepress/theme/custom/record.vue'
</script>

错误格式

md
<template :class="$style.timeline" v-for="[year,monthly] in annual">
    ## {{year}}年
    <template v-for="[month,records] in monthly">
        ### {{Number(month)}}月
        <template v-for="record in records">
            <template v-if="!!record.article">
                <p>
                    <strong>{{Number(record.date.substr(8,2))}}日<a :href="record.article">[{{record.article.slice(record.article.lastIndexOf('/')+1)}}]</a></strong>{{record.record}}
                </p>
            </template>
            <template v-else>
                <p><strong>{{Number(record.date.substr(8,2))}}日</strong>{{record.record}}</p>
            </template>
        </template>
    </template>
</template>

正确格式

md
<template :class="$style.timeline" v-for="[year,monthly] in annual">
    
## {{year}}年
<template v-for="[month,records] in monthly">

### {{Number(month)}}月 
<template v-for="record in records">
<template v-if="!!record.article">
<p>
<strong>{{Number(record.date.substr(8,2))}}日<a :href="record.article">[{{record.article.slice(record.article.lastIndexOf('/')+1)}}]</a></strong>{{record.record}}
</p>
</template>
<template v-else>
<p><strong>{{Number(record.date.substr(8,2))}}日</strong>{{record.record}}</p>
</template>
</template>
</template>
</template>

使用css

VitePress不推荐在md中使用<style scoped>,建议使用<style module>

md
<div :class="$style.timeline">
    //...
<div>
<style module>
.timeline {
    //...
}
</style>

2025-01-26