Skip to content
第 134 / 250 章前端⏱ 10 分钟阅读

第 134 章:组件库发布

学习目标

  • 掌握 npm 包的发布流程
  • 学会 Monorepo 管理与版本控制
  • 理解语义化版本、Changesets、CI 发布
  • 搭建组件库文档站

一、为什么要发布组件库

二、库模式构建

2.1 vite.config.ts

typescript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'MyUI',
      fileName: (format) => `my-ui.${format}.js`,
      formats: ['es', 'cjs', 'umd']
    },
    rollupOptions: {
      external: ['vue'],
      output: {
        globals: { vue: 'Vue' },
        assetFileNames: 'my-ui.[name].[ext]'
      }
    },
    sourcemap: true,
    emptyOutDir: false
  }
});

2.2 package.json

json
{
  "name": "my-ui",
  "version": "1.0.0",
  "description": "My UI Component Library",
  "type": "module",
  "files": [
    "dist",
    "*.d.ts",
    "README.md"
  ],
  "main": "./dist/my-ui.cjs.js",
  "module": "./dist/my-ui.es.js",
  "unpkg": "./dist/my-ui.umd.js",
  "jsdelivr": "./dist/my-ui.umd.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/my-ui.es.js",
      "require": "./dist/my-ui.cjs.js"
    },
    "./style.css": "./dist/style.css"
  },
  "scripts": {
    "build": "vue-tsc --emitDeclarationOnly && vite build",
    "dev": "vite",
    "preview": "vite preview"
  },
  "peerDependencies": {
    "vue": "^3.0.0"
  },
  "keywords": ["vue", "ui", "components"],
  "license": "MIT"
}

2.3 入口文件

typescript
// src/index.ts
import type { App } from 'vue';
import MyButton from './components/Button.vue';
import MyInput from './components/Input.vue';

const components = [MyButton, MyInput];

export { MyButton, MyInput };

export default {
  install(app: App) {
    components.forEach((comp) => {
      app.component(comp.__name || 'Comp', comp);
    });
  }
};

2.4 类型声明

json
// package.json
{
  "scripts": {
    "build:types": "vue-tsc --emitDeclarationOnly --declaration --outDir dist"
  }
}

三、Monorepo 架构

3.1 pnpm workspace

yaml
# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'play'

3.2 目录结构

my-ui/
├── packages/
│   ├── components/
│   │   ├── src/
│   │   │   ├── Button/
│   │   │   ├── Input/
│   │   │   └── index.ts
│   │   └── package.json
│   ├── theme/
│   │   ├── src/
│   │   └── package.json
│   └── utils/
│       ├── src/
│       └── package.json
├── play/                # 测试用
│   ├── src/
│   └── package.json
├── docs/                # 文档
├── scripts/
├── package.json
└── pnpm-workspace.yaml

3.3 子包 package.json

json
// packages/components/package.json
{
  "name": "@my-ui/components",
  "version": "1.0.0",
  "main": "src/index.ts",
  "types": "src/index.ts",
  "dependencies": {
    "@my-ui/utils": "workspace:*",
    "@my-ui/theme": "workspace:*",
    "vue": "^3.0.0"
  }
}

3.4 根 package.json

json
{
  "name": "my-ui-monorepo",
  "private": true,
  "scripts": {
    "dev": "pnpm --filter play dev",
    "build": "pnpm -r build",
    "build:components": "pnpm --filter @my-ui/components build",
    "lint": "eslint \"packages/**/*.{ts,vue}\"",
    "test": "vitest",
    "release": "pnpm build && changeset publish"
  }
}

四、版本管理

4.1 语义化版本

主版本.次版本.修订号
  ↓       ↓      ↓
  1  .   2  .   3
  • 主版本(MAJOR):不兼容 API 变更
  • 次版本(MINOR):向后兼容的功能新增
  • 修订号(PATCH):向后兼容的 bug 修复

4.2 预发布版本

1.0.0-alpha.0
1.0.0-beta.1
1.0.0-rc.0

4.3 Changesets

bash
pnpm add -D @changesets/cli
pnpm changeset init

4.4 添加变更

bash
pnpm changeset
markdown
# .changeset/abc123.md
---
'@my-ui/components': minor
---

新增 Button 组件的 loading 属性

4.5 发布

bash
pnpm changeset version   # 更新版本
pnpm changeset publish   # 发布到 npm

4.6 Changesets CI

yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      - run: pnpm test
      - run: pnpm changeset version
      - run: pnpm changeset publish
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

五、文档站

5.1 VitePress

bash
pnpm add -D vitepress

5.2 配置

typescript
// docs/.vitepress/config.ts
import { defineConfig } from 'vitepress';

export default defineConfig({
  title: 'MyUI',
  themeConfig: {
    nav: [
      { text: '指南', link: '/guide/' },
      { text: '组件', link: '/components/button' }
    ],
    sidebar: {
      '/components/': [
        {
          text: '基础组件',
          items: [
            { text: 'Button 按钮', link: '/components/button' },
            { text: 'Input 输入', link: '/components/input' }
          ]
        }
      ]
    }
  }
});

5.3 组件示例

markdown
# Button 按钮

常用的操作按钮。

## 基础用法

<div class="example">
  <MyButton>默认</MyButton>
  <MyButton type="primary">主要</MyButton>
</div>

::: details 查看代码

```vue
<template>
  <MyButton>默认</MyButton>
  <MyButton type="primary">主要</MyButton>
</template>

:::

API

参数说明类型默认值
type类型'primary' | 'success'default
size尺寸'small' | 'medium' | 'large'medium

### 5.4 自动注册

```typescript
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme';
import MyUI from '../../../packages/components/src';
import 'my-ui/dist/style.css';

export default {
  extends: DefaultTheme,
  enhanceApp({ app }) {
    app.use(MyUI);
  }
};

六、构建与发布

6.1 构建脚本

typescript
// scripts/build.ts
import { execSync } from 'child_process';

const packages = ['components', 'theme', 'utils'];

for (const pkg of packages) {
  console.log(`Building ${pkg}...`);
  execSync(`pnpm --filter @my-ui/${pkg} build`, { stdio: 'inherit' });
}

6.2 发布脚本

bash
# .github/workflows/publish.yml
name: Publish

on:
  push:
    branches: [main]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org/

      - run: pnpm install
      - run: pnpm build
      - run: pnpm publish -r --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

6.3 预发布

bash
# alpha 版本
pnpm changeset pre enter alpha
pnpm changeset version
pnpm changeset publish --tag alpha

# 退出预发布
pnpm changeset pre exit

七、CI 完整流程

八、CDN 分发

8.1 jsDelivr

html
<!-- 加载 UMD 打包 -->
<script src="https://cdn.jsdelivr.net/npm/my-ui@1.0.0/dist/my-ui.umd.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/my-ui@1.0.0/dist/style.css" />

8.2 unpkg

html
<script src="https://unpkg.com/my-ui@1.0.0/dist/my-ui.umd.js"></script>

8.3 ESM CDN

html
<script type="module">
  import { MyButton } from 'https://cdn.jsdelivr.net/npm/my-ui@1.0.0/dist/my-ui.es.js';
</script>

九、Playground

9.1 内部测试

typescript
// play/src/App.vue
<script setup lang="ts">
import { MyButton } from '@my-ui/components';
</script>

<template>
  <div class="playground">
    <MyButton>测试</MyButton>
  </div>
</template>

9.2 运行

bash
pnpm --filter play dev

十、注意事项

10.1 样式导出

typescript
// 主入口
import './styles/index.css';

export { MyButton, MyInput } from './components';

10.2 Tree-shaking

typescript
// ✅ 每个组件单独 export
export { MyButton } from './Button';
export { MyInput } from './Input';

// ❌ 避免循环引用

10.3 依赖 Peer

json
{
  "peerDependencies": {
    "vue": "^3.0.0"
  }
}

10.4 文件体积

bash
pnpm add -D rollup-plugin-visualizer
typescript
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({ open: true })
  ]
});

十一、常见问题

11.1 样式丢失

typescript
// 用户必须手动导入
import 'my-ui/dist/style.css';

11.2 类型错误

bash
# 生成 .d.ts
pnpm vue-tsc --emitDeclarationOnly

11.3 版本冲突

bash
# 锁定依赖
pnpm install --frozen-lockfile

11.4 路径别名

typescript
// vite.config.ts
{
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
}

十二、推荐工具

工具用途
Changesets版本管理
VitePress文档站
Rollup打包
TypeScript类型
Storybook组件预览
Size Limit体积控制

十三、本章小结

概念关键
库模式build.lib + 多格式
Monorepopnpm workspace
版本管理Changesets
文档站VitePress
自动化GitHub Actions
发布npm + tag

动手练习

  1. 库构建:将你的组件库配置为库模式
  2. Monorepo:用 pnpm workspace 管理多个包
  3. Changesets:为每次变更添加 changeset
  4. 文档站:用 VitePress 搭建组件库文档
  5. CI 发布:配置 GitHub Actions 自动发布

推荐阅读


下一章第 135 章:性能优化

本站基于 VitePress 构建 · 由 Codebook 团队维护