主题打包与发布
本指南将教你如何将开发的 VitePress 主题打包并发布到 npm,让其他用户可以轻松安装和使用你的主题。
版本说明
- 本文档基于 VitePress v1.0.0+ 编写
- 需要 Node.js 18.0.0 及以上版本
- 需要 npm 账号(发布到 npm)
- 支持打包 TypeScript 和 Vue 组件
主题项目结构
标准目录结构
my-vitepress-theme/
├── src/
│ ├── index.ts # 主题入口文件
│ ├── Layout.vue # 主布局组件
│ ├── components/ # 组件目录
│ │ ├── Navbar.vue
│ │ ├── Sidebar.vue
│ │ └── ...
│ ├── composables/ # 组合式函数
│ │ ├── useData.ts
│ │ └── useTheme.ts
│ ├── styles/ # 样式文件
│ │ ├── index.css
│ │ ├── variables.css
│ │ └── components.css
│ ├── utils/ # 工具函数
│ │ └── helper.ts
│ └── types/ # TypeScript 类型
│ └── index.ts
├── dist/ # 打包输出目录
├── package.json # 包配置文件
├── tsconfig.json # TypeScript 配置
├── vite.config.ts # Vite 打包配置
├── README.md # 文档
├── LICENSE # 许可证
└── .npmignore # npm 忽略文件主题入口文件
typescript
// src/index.ts
import type { Theme } from 'vitepress'
import Layout from './Layout.vue'
import Navbar from './components/Navbar.vue'
import Sidebar from './components/Sidebar.vue'
// 导入样式
import './styles/index.css'
// 导出类型
export * from './types'
// 导出组件
export { default as Layout } from './Layout.vue'
export { default as Navbar } from './components/Navbar.vue'
export { default as Sidebar } from './components/Sidebar.vue'
// 导出 composables
export * from './composables/useData'
export * from './composables/useTheme'
// 导出工具函数
export * from './utils/helper'
// 主题配置
const theme: Theme = {
Layout,
enhanceApp({ app }) {
// 注册全局组件
app.component('Navbar', Navbar)
app.component('Sidebar', Sidebar)
}
}
export default theme配置 package.json
基础配置
json
{
"name": "vitepress-theme-my-theme",
"version": "1.0.0",
"description": "一个精美的 VitePress 博客主题",
"author": "Your Name <your.email@example.com>",
"license": "MIT",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.umd.cjs"
},
"./style.css": "./dist/style.css",
"./components/*": "./dist/components/*"
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"sideEffects": [
"**/*.css"
],
"keywords": [
"vitepress",
"theme",
"blog",
"documentation",
"vue"
],
"repository": {
"type": "git",
"url": "https://github.com/yourusername/vitepress-theme-my-theme"
},
"bugs": {
"url": "https://github.com/yourusername/vitepress-theme-my-theme/issues"
},
"homepage": "https://github.com/yourusername/vitepress-theme-my-theme#readme",
"scripts": {
"dev": "vite build --watch",
"build": "vite build && npm run build:types",
"build:types": "vue-tsc --emitDeclarationOnly",
"prepublishOnly": "npm run build"
},
"peerDependencies": {
"vitepress": "^1.0.0",
"vue": "^3.3.0"
},
"devDependencies": {
"vite": "^5.0.0",
"vitepress": "^1.0.0",
"vue": "^3.3.0",
"vue-tsc": "^1.8.0",
"typescript": "^5.0.0"
}
}重要字段说明
1. exports 字段
json
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.umd.cjs"
},
"./style.css": "./dist/style.css"
}
}- 定义了包的导出入口点
- 支持 ESM 和 UMD 两种模块格式
- 提供类型声明文件
- 单独导出样式文件
2. files 字段
json
{
"files": [
"dist",
"README.md",
"LICENSE"
]
}- 指定发布到 npm 的文件
- 避免发布不必要的文件
3. peerDependencies 字段
json
{
"peerDependencies": {
"vitepress": "^1.0.0",
"vue": "^3.3.0"
}
}- 指定兼容的宿主包版本
- 避免重复安装依赖
4. sideEffects 字段
json
{
"sideEffects": [
"**/*.css"
]
}- 标记 CSS 文件有副作用
- 支持 Tree Shaking
配置 Vite 打包
vite.config.ts
typescript
import { defineConfig } from 'vite'
import { resolve } from 'path'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue()
],
build: {
lib: {
// 入口文件
entry: resolve(__dirname, 'src/index.ts'),
// 包名
name: 'VitePressThemeMyTheme',
// 输出文件名
fileName: (format) => {
return format === 'es' ? 'index.js' : 'index.umd.cjs'
}
},
rollupOptions: {
// 外部化依赖
external: ['vue', 'vitepress'],
output: {
// 全局变量映射
globals: {
vue: 'Vue',
vitepress: 'VitePress'
},
// 保留导入语句
exports: 'named',
// 代码分割
manualChunks: undefined
}
},
// 启用 CSS 代码分割
cssCodeSplit: true,
// 生成 sourcemap
sourcemap: true,
// 压缩选项
minify: 'esbuild',
// 目标浏览器
target: 'esnext'
},
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
}
})TypeScript 配置
json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["ESNext", "DOM"],
"moduleResolution": "bundler",
"strict": true,
"jsx": "preserve",
"declaration": true,
"declarationDir": "./dist",
"emitDeclarationOnly": true,
"outDir": "./dist",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.vue"
],
"exclude": [
"node_modules",
"dist"
]
}类型声明
类型声明文件
typescript
// src/types/index.ts
import type { Theme, EnhanceAppContext } from 'vitepress'
import type { Component } from 'vue'
export interface ThemeConfig {
/**
* Logo 图片路径
*/
logo?: string
/**
* 站点名称
*/
siteTitle?: string
/**
* 导航配置
*/
nav?: NavItem[]
/**
* 侧边栏配置
*/
sidebar?: SidebarConfig
/**
* 社交链接
*/
socialLinks?: SocialLink[]
/**
* 页脚配置
*/
footer?: FooterConfig
/**
* 是否显示文章目录
*/
outline?: boolean | OutlineConfig
/**
* 评论配置
*/
comment?: CommentConfig
}
export interface NavItem {
text: string
link?: string
items?: NavItem[]
activeMatch?: string
}
export interface SidebarConfig {
[path: string]: SidebarItem[]
}
export interface SidebarItem {
text: string
link?: string
items?: SidebarItem[]
collapsed?: boolean
}
export interface SocialLink {
icon: string
link: string
}
export interface FooterConfig {
message?: string
copyright?: string
}
export interface OutlineConfig {
level: number | [number, number]
label?: string
}
export interface CommentConfig {
/**
* 评论服务提供商
*/
provider: 'giscus' | 'waline' | 'utterances'
/**
* Giscus 配置
*/
giscus?: {
repo: string
repoId: string
category: string
categoryId: string
}
/**
* Waline 配置
*/
waline?: {
serverURL: string
}
}
// 导出 Vue 组件类型
export { default as Layout } from '../Layout.vue'
export { default as Navbar } from '../components/Navbar.vue'
export { default as Sidebar } from '../components/Sidebar.vue'Vue 组件类型声明
typescript
// src/shims-vue.d.ts
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<object, object, unknown>
export default component
}打包流程
构建脚本
bash
# 安装依赖
npm install
# 开发模式(监听文件变化)
npm run dev
# 生产构建
npm run build
# 生成类型声明
npm run build:types构建输出
dist/
├── index.js # ESM 格式
├── index.umd.cjs # UMD 格式
├── index.d.ts # 类型声明
├── style.css # 样式文件
├── components/ # 组件类型声明
│ ├── Navbar.d.ts
│ ├── Sidebar.d.ts
│ └── ...
└── composables/ # Composables 类型声明
├── useData.d.ts
├── useTheme.d.ts
└── ...发布到 npm
准备工作
1. 注册 npm 账号
bash
# 访问 npmjs.com 注册账号
# 或使用命令行注册
npm adduser2. 登录 npm
bash
npm login
# 输入用户名、密码、邮箱
# 验证邮箱3. 验证登录状态
bash
npm whoami
# 输出: yourusername发布流程
1. 检查包名
bash
# 检查包名是否已被占用
npm search vitepress-theme-my-theme2. 更新版本号
bash
# 遵循语义化版本规范
# 主版本号.次版本号.修订号
# 更新修订号(修复 bug)
npm version patch
# 更新次版本号(新增功能,向后兼容)
npm version minor
# 更新主版本号(破坏性更新)
npm version major3. 发布
bash
# 首次发布
npm publish
# 发布到 next 标签(测试版)
npm publish --tag next
# 发布到 beta 标签
npm publish --tag beta
# 发布 scoped package(@username/package-name)
npm publish --access public4. 验证发布
bash
# 查看包信息
npm info vitepress-theme-my-theme
# 安装测试
npm install vitepress-theme-my-theme.npmignore 配置
# 开发文件
src/
*.ts
!*.d.ts
tsconfig.json
vite.config.ts
# 测试文件
__tests__/
*.test.ts
*.spec.ts
coverage/
# 配置文件
.gitignore
.eslintrc.js
.prettierrc
.editorconfig
# 文档
docs/
*.md
!README.md
# 其他
node_modules/
.DS_Store
*.log版本管理
语义化版本
MAJOR.MINOR.PATCH
MAJOR: 主版本号,不兼容的 API 修改
MINOR: 次版本号,向后兼容的功能性新增
PATCH: 修订号,向后兼容的问题修正版本更新策略
bash
# 示例版本更新流程
# 初始版本
1.0.0
# 修复 bug -> 更新 PATCH
1.0.1
1.0.2
# 新增功能 -> 更新 MINOR
1.1.0
1.1.1 (包含 bug 修复)
# 破坏性更新 -> 更新 MAJOR
2.0.0预发布版本
bash
# Alpha 版本(内部测试)
1.0.0-alpha.1
1.0.0-alpha.2
# Beta 版本(公开测试)
1.0.0-beta.1
1.0.0-beta.2
# RC 版本(候选发布)
1.0.0-rc.1
1.0.0-rc.2
# 发布预发布版本
npm publish --tag alpha
npm publish --tag beta
npm publish --tag rc更新和维护
更新流程
bash
# 1. 更新代码
git add .
git commit -m "feat: add new feature"
# 2. 更新版本号
npm version minor
# 3. 推送到 Git
git push origin main
git push --tags
# 4. 发布到 npm
npm publish
# 5. 创建 GitHub Release
# 在 GitHub 上创建新的 ReleaseCHANGELOG 维护
markdown
# CHANGELOG.md
# Changelog
All notable changes to this project will be documented in this file.
## [1.1.0] - 2024-01-20
### Added
- 新增暗黑模式支持
- 新增文章目录组件
- 新增评论系统集成
### Changed
- 优化首页加载性能
- 更新导航栏样式
### Fixed
- 修复移动端菜单显示问题
- 修复侧边栏滚动异常
## [1.0.0] - 2024-01-01
### Added
- 初始版本发布
- 基础主题功能
- 文章列表和详情页自动化发布
使用 GitHub Actions 自动发布:
yaml
# .github/workflows/publish.yml
name: Publish to npm
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Publish
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}使用文档
README.md 模板
markdown
# vitepress-theme-my-theme
一个精美的 VitePress 博客主题,支持暗黑模式、评论系统、文章归档等功能。
## 特性
- 🎨 **精美设计** - 现代化的 UI 设计,支持暗黑模式
- 📝 **博客功能** - 完整的博客功能,包括文章列表、标签、归档
- 💬 **评论系统** - 支持 Giscus、Waline 等评论系统
- 📱 **响应式** - 完美适配移动端和桌面端
- ⚡️ **高性能** - 基于 Vite 构建,快速加载
- 🔧 **可配置** - 灵活的配置选项
## 安装
```bash
npm install vitepress-theme-my-theme
# 或
yarn add vitepress-theme-my-theme
# 或
pnpm add vitepress-theme-my-theme快速开始
1. 创建配置文件
typescript
// .vitepress/theme/index.ts
import { defineTheme } from 'vitepress-theme-my-theme'
export default defineTheme({
// 主题配置
logo: '/logo.svg',
siteTitle: '我的博客',
nav: [
{ text: '首页', link: '/' },
{ text: '归档', link: '/archives' },
{ text: '关于', link: '/about' }
],
comment: {
provider: 'giscus',
giscus: {
repo: 'yourusername/yourrepo',
repoId: 'your-repo-id',
category: 'Announcements',
categoryId: 'your-category-id'
}
}
})2. 创建文章
markdown
---
title: 我的第一篇文章
date: 2024-01-20
tags:
- 教程
- VitePress
---
文章内容...文档
示例
查看 在线示例
贡献
欢迎贡献代码!请查看 贡献指南
许可证
作者
Your Name - @yourusername
致谢
## 最佳实践
### 1. 代码质量
- ✅ 使用 TypeScript 编写,提供完整类型
- ✅ 编写单元测试
- ✅ 使用 ESLint 和 Prettier 格式化代码
- ✅ 编写详细的文档和注释
### 2. 性能优化
- ✅ 使用 Tree Shaking
- ✅ 按需加载组件
- ✅ 压缩代码和样式
- ✅ 优化图片资源
### 3. 用户体验
- ✅ 提供详细的文档
- ✅ 提供示例项目
- ✅ 快速响应 Issues
- ✅ 定期更新维护
### 4. 发布规范
- ✅ 遵循语义化版本
- ✅ 维护 CHANGELOG
- ✅ 提供迁移指南
- ✅ 标记预发布版本
## 故障排查
### 发布失败
```bash
# 检查包名是否已被占用
npm search your-package-name
# 检查登录状态
npm whoami
# 检查 npm 配置
npm config list
# 检查包内容
npm pack --dry-run类型错误
bash
# 重新生成类型声明
npm run build:types
# 检查 tsconfig.json 配置
# 确保 declaration: true样式问题
bash
# 确保在 sideEffects 中声明 CSS
# 确保正确导出样式文件
import 'vitepress-theme-my-theme/style.css'相关资源
下一步
- VitePress 主题架构原理 - 深入理解主题系统
- 主题开发常见问题 - 解决开发中的常见问题
- 主题性能优化 - 优化主题性能