开发 VitePress 插件
学习如何开发 VitePress 插件来扩展站点功能。
插件类型
Vite 插件
VitePress 支持标准 Vite 插件:
ts
// .vitepress/config.mts
export default defineConfig({
vite: {
plugins: [
myVitePlugin()
]
}
})Markdown 插件
通过 markdown 配置添加 Markdown-it 插件:
ts
export default defineConfig({
markdown: {
config(md) {
md.use(myMarkdownPlugin)
}
}
})构建钩子
使用配置中的钩子函数:
ts
export default defineConfig({
transformHead(context) {
// 修改 head
},
transformHtml(code, id, context) {
// 修改 HTML
},
buildEnd(siteConfig) {
// 构建结束
}
})创建 Vite 插件
基础结构
ts
// plugins/my-plugin.ts
import type { Plugin } from 'vite'
export function myPlugin(options = {}): Plugin {
return {
name: 'vitepress-plugin-my',
// 配置解析后
configResolved(config) {
console.log('插件加载')
},
// 转换代码
transform(code, id) {
if (id.endsWith('.md')) {
return transformMarkdown(code)
}
return null
},
// 构建开始
buildStart() {
console.log('构建开始')
},
// 构建结束
buildEnd() {
console.log('构建结束')
}
}
}
function transformMarkdown(code: string): string {
// 转换 Markdown 内容
return code.replace(/{{\s*(\w+)\s*}}/g, (_, key) => {
return `<span class="var-${key}">${key}</span>`
})
}在 VitePress 中使用
ts
// .vitepress/config.mts
import { myPlugin } from './plugins/my-plugin'
export default defineConfig({
vite: {
plugins: [
myPlugin({
// 配置选项
})
]
}
})创建 Markdown 插件
基础结构
ts
// plugins/markdown-plugin.ts
import type MarkdownIt from 'markdown-it'
export function markdownPlugin(md: MarkdownIt) {
// 添加自定义块
md.block.ruler.before('paragraph', 'myblock', (state, startLine, endLine) => {
const pos = state.bMarks[startLine] + state.tShift[startLine]
const max = state.eMarks[startLine]
if (state.src.slice(pos, max) !== ':::myblock') {
return false
}
// 解析自定义块
let nextLine = startLine + 1
while (nextLine < endLine) {
const linePos = state.bMarks[nextLine] + state.tShift[nextLine]
const lineMax = state.eMarks[nextLine]
if (state.src.slice(linePos, lineMax) === ':::') {
break
}
nextLine++
}
const content = state.getLines(startLine + 1, nextLine, state.blkIndent, true)
const token = state.push('myblock', 'div', 0)
token.content = content
token.map = [startLine, nextLine]
state.line = nextLine + 1
return true
})
// 渲染规则
md.renderer.rules.myblock = (tokens, idx) => {
return `<div class="my-block">${tokens[idx].content}</div>`
}
}在 VitePress 中使用
ts
export default defineConfig({
markdown: {
config(md) {
md.use(markdownPlugin)
}
}
})构建钩子示例
生成 RSS
ts
// .vitepress/config.mts
import { writeFileSync } from 'fs'
export default defineConfig({
async buildEnd({ outDir, siteConfig }) {
// 读取所有页面
const pages = await getPages(outDir)
// 生成 RSS
const rss = generateRSS(pages)
writeFileSync(`${outDir}/rss.xml`, rss)
}
})注入全局数据
ts
export default defineConfig({
transformPageData(pageData) {
// 添加自定义字段
pageData.customField = 'value'
// 计算阅读时间
pageData.readingTime = calculateReadingTime(pageData.content)
}
})完整插件示例
图片优化插件
ts
// plugins/image-optimization.ts
import type { Plugin } from 'vite'
import sharp from 'sharp'
import fs from 'fs'
import path from 'path'
interface ImageOptimizationOptions {
quality?: number
formats?: ('webp' | 'avif')[]
sizes?: number[]
}
export function imageOptimization(
options: ImageOptimizationOptions = {}
): Plugin {
const {
quality = 80,
formats = ['webp'],
sizes = [640, 1024, 1920]
} = options
return {
name: 'vitepress-plugin-image-optimization',
async buildEnd() {
const publicDir = path.resolve('docs/public')
const images = findImages(publicDir)
for (const image of images) {
const ext = path.extname(image)
const base = path.basename(image, ext)
const dir = path.dirname(image)
for (const format of formats) {
for (const size of sizes) {
const outputPath = path.join(
dir,
`${base}-${size}w.${format}`
)
await sharp(image)
.resize(size)
.toFormat(format, { quality })
.toFile(outputPath)
console.log(`Generated: ${outputPath}`)
}
}
}
}
}
}
function findImages(dir: string): string[] {
const images: string[] = []
const entries = fs.readdirSync(dir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
images.push(...findImages(fullPath))
} else if (/\.(png|jpe?g)$/i.test(entry.name)) {
images.push(fullPath)
}
}
return images
}使用插件
ts
import { imageOptimization } from './plugins/image-optimization'
export default defineConfig({
vite: {
plugins: [
imageOptimization({
quality: 80,
formats: ['webp'],
sizes: [640, 1024]
})
]
}
})发布插件
package.json
json
{
"name": "vitepress-plugin-my",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"files": ["dist"],
"peerDependencies": {
"vitepress": ">=1.0.0"
},
"keywords": [
"vitepress",
"plugin"
]
}学习检查清单
- [ ] 理解了插件类型
- [ ] 创建了 Vite 插件
- [ ] 创建了 Markdown 插件
- [ ] 使用了构建钩子
- [ ] 发布了插件到 npm
下一步
继续学习 构建组件文档站,为 Vue 组件创建文档。