Skip to content

主题打包与发布

本指南将教你如何将开发的 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 adduser

2. 登录 npm

bash
npm login
# 输入用户名、密码、邮箱
# 验证邮箱

3. 验证登录状态

bash
npm whoami
# 输出: yourusername

发布流程

1. 检查包名

bash
# 检查包名是否已被占用
npm search vitepress-theme-my-theme

2. 更新版本号

bash
# 遵循语义化版本规范
# 主版本号.次版本号.修订号

# 更新修订号(修复 bug)
npm version patch

# 更新次版本号(新增功能,向后兼容)
npm version minor

# 更新主版本号(破坏性更新)
npm version major

3. 发布

bash
# 首次发布
npm publish

# 发布到 next 标签(测试版)
npm publish --tag next

# 发布到 beta 标签
npm publish --tag beta

# 发布 scoped package(@username/package-name)
npm publish --access public

4. 验证发布

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 上创建新的 Release

CHANGELOG 维护

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
---

文章内容...

文档

示例

查看 在线示例

贡献

欢迎贡献代码!请查看 贡献指南

许可证

MIT

作者

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'

相关资源

下一步

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献