创建插件以实现首页文章的自动更新
TIP
在VitePress原版案例中,首页三个文本框由features
定义,仅能实现静态文字展示,所以我想通过自己编写vue插件来实现首页文章的自动更新。
这个插件可以干什么?
自动识别新增的文章并显示在首页指定的栏目中。
每个栏目的标题、副标题、字号、行间距、分隔符、显示文章数量等皆可自定义。
安装依赖
该功能需要用到三个依赖:fast-glob
、gray-matter
、dayjs
。
TIP
- fast-glob:高性能递归读取文件(比 Node 原生快,支持通配符 **/*.md)。
- gray-matter:把 Markdown 头部的 --- 区域解析成 JSON(拿标题、日期)。
- dayjs:把不同格式的日期统一成时间戳,方便排序。
安装命令:
bash
pnpm add -D fast-glob gray-matter dayjs
创建插件
编辑DynamicFeatureBox.vue
js
<script setup lang="ts">
import { computed } from 'vue'
import { data } from '../../data/posts.data.mjs'
interface Props {
title: string
subTitle?: string
folder: string
max?: number
}
const props = withDefaults(defineProps<Props>(), { max: 3 })
const list = computed(() =>
data
.filter(p => p.file.startsWith(props.folder))
.sort((a, b) => +new Date(b.date) - +new Date(a.date))
.slice(0, props.max)
)
</script>
<template>
<div class="feature-box">
<!-- 主标题 -->
<h3 class="feature-title">{{ title }}</h3>
<!-- 副标题(选配) -->
<p v-if="subTitle" class="feature-subtitle">{{ subTitle }}</p>
<!-- 文章列表 -->
<ul v-if="list.length" class="feature-list">
<li v-for="(item, idx) in list" :key="item.url">
<span v-if="idx === 0" class="new-badge">🔥‼️</span>
<a :href="item.url">{{ item.title }}</a>
</li>
</ul>
<!-- 空状态 -->
<p v-else class="empty-tip">暂无文章</p>
</div>
</template>
<style scoped>
.feature-box {
flex: 1;
padding: 1.5rem 1.25rem;
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
background: var(--vp-c-bg-soft);
}
.feature-title {
margin: 0 0 0.25rem;
font-size: 1.25rem;
color: var(--vp-c-brand);
}
.feature-subtitle {
margin: 0 0 0.75rem;
font-size: 1.0rem;
color: var(--vp-c-text-2);
}
.feature-list {
margin: 0;
padding: 0;
list-style: none;
}
.feature-list li {
margin: 0.2rem 0;
font-size: 0.95rem;
border-bottom: 1px solid rgb(219, 219, 219);
padding-bottom: 0.1em;
}
.feature-list li:last-child {
border-bottom: none;
}
.new-badge {
color: #f43f5e;
font-size: 0.8rem;
margin-right: 0.25rem;
}
.empty-tip {
margin: 0;
font-size: 0.9rem;
color: var(--vp-c-text-3);
font-style: italic;
}
a {
color: var(--vp-c-text-1);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
编辑posts.data.mts
TIP
posts.data.mts要放在/.vitepress/data中。
js
import { defineLoader } from 'vitepress'
import fg from 'fast-glob'
import matter from 'gray-matter'
import fs from 'fs'
import path from 'path'
export default defineLoader({
watch: ['**/*.md'], // 监听根目录所有 md
async load() {
const files = await fg('**/*.md', {
ignore: ['node_modules/**', '.vitepress/**', 'dist/**']
})
return files.map(file => {
const src = fs.readFileSync(path.resolve(file), 'utf-8')
const { data: front } = matter(src)
return {
title: front.title || path.basename(file, '.md'),
date: front.date || fs.statSync(path.resolve(file)).mtime,
file,
url: '/' + file.replace(/(^|\/)index\.md$/, '').replace(/\.md$/, '')
}
})
}
})
注册插件
js
import DynamicFeatureBox from './components/DynamicFeatureBox.vue'
app.component('DynamicFeatureBox', DynamicFeatureBox)
使用插件
替换掉原有的features
。
html
<script setup>
import DynamicFeatureBox from './.vitepress/theme/components/DynamicFeatureBox.vue'
</script>
<div class="features-container">
<DynamicFeatureBox title="🥳 最新公告 📣" sub-title="⏱️ 站点动态与重要通知" folder="doc_notic" :max="3" />
<DynamicFeatureBox title="📚 知识仓库 🧐" sub-title="⏳ 搜集各类知识点与小技巧" folder="doc_wiki" :max="3" />
<DynamicFeatureBox title="💡 社区文章 📝" sub-title="🎞️ 用户分享与经验交流" folder="doc_doc" :max="3" />
</div>
<style scoped>
.features-container {
display: flex;
gap: 1.5rem;
flex-wrap: wrap;
margin-top: 2.5rem;
}
@media (max-width: 768px) {
.features-container {
flex-direction: column;
}
}
</style>