将 Vue 组件库打包为 npm 包

如果想要在多个项目中使用同样的组件的话,每次手动导入会比较麻烦,使用 npm 包是一种便捷的做法。

不过,打包 npm 包的行为和打包为网页不太一样,我们不能够简单地直接把 build 的成果直接当做 npm 包。有几个地方需要注意,下面我把自己打包过程中的几个点总结一下。

package.json 的配置

小白需要先理解 package.json 到底是什么。package.json 是每个 npm 包的核心配置文件,它描述了包的各种信息,比如名称、版本、入口文件、依赖等。package.json 文件用于帮助 npm 理解包的内容。对于打包成 npm 包来说,package.json 中几个字段非常关键,尤其是以下几个:

  • "name":包的名称,在发布到 npm 上时不能重复。
  • "version":版本号,每次发布新版本时必须更新。
  • "main":指向 CommonJS 格式的入口文件,通常是打包后的 index.jsdist/index.js。(兼容旧工具用)
  • "module":指向 ES Module 格式的入口文件,很多现代打包器(如 Vite、Rollup)会优先使用它。(兼容旧工具用)
  • "types":指向类型声明文件(如果你用 TypeScript 开发),例如 dist/index.d.ts。(兼容旧工具用)
  • "exports":推荐使用的新字段,用于精细控制包的导出入口。

exportspackage.json 中一个相对较新的字段,用于明确指定包的对外接口。它不仅可以指定主入口,还可以为不同的环境(比如 ESM、CJS、TypeScript)分别定义入口,甚至可以限制哪些文件能被外部导入,从而提升包的封装性和安全性。

示例 package.json (部分字段):

{
"name": "vue-window",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"style": "dist/index.css",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./dist/index.css": "./dist/index.css",
"./package.json": "./package.json"
},
"files": [
"dist"
],
}

这个配置表示:

  • 当用户 import 'your-package' 时,使用 index.mjs
  • 当用户 require('your-package') 时,使用 index.cjs
  • 类型提示来自 index.d.ts

tsconfig.json 的配置

Vue 项目由 vue-tsc 进行类型检查,并生成类型文件 .d.tsvue-tsc 替代了 TypeScript 官方的 tsc 工具。不过,默认情况下,因为网页是不需要类型文件的,所以按照默认配置并不会生成类型文件。我们需要修改 tsconfig.json 文件。

下面是一个推荐的 tsconfig.json 配置,并逐项解释其用途。

📄 推荐的 tsconfig.json(用于 npm 库):

{
  "compilerOptions": {
    "target": "ES2017",                  
    "module": "ESNext",                 
    "moduleResolution": "Node",        
    "declaration": true,                // ✅ 生成 .d.ts 类型声明文件
    "declarationDir": "./dist/types",  // 类型文件输出目录
    "outDir": "./dist",                 // 输出 JS 到 dist 目录
    "emitDeclarationOnly": false,      // 若只想生成类型文件可设为 true
    "strict": true,                    // 开启严格模式(推荐)
    "esModuleInterop": true,            
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
  },
  "include": ["src"],                  // 要处理的文件目录
  "exclude": ["node_modules", "dist"]  // 不要处理的文件目录
}

🛠️ 说明:

  • "declaration": true 是打包 npm 包最关键的一项,必须开启才能生成 .d.ts 文件。
  • "outDir" 应该和打包工具(如 Rollup、tsup)输出的路径一致,一般是 dist/
  • "emitDeclarationOnly" 如果你只用 TypeScript 生成类型,JS 部分交给打包工具处理,可以设为 true
  • "module": "ESNext" + "moduleResolution": "Node" 是现代库的标准写法,能支持更好的 tree-shaking。
  • "esModuleInterop""allowSyntheticDefaultImports" 能提高与旧模块的兼容性。

⚠️ 小提醒:你最终生成的 dist/ 目录结构,应该和 package.json 中的 exports 字段相匹配,包括类型声明路径。

⚠️ 警告:不要包含自带的 tsconfig.app.jsontsconfig.node.json

vite.config.ts 的配置

vite 用来打包项目的 js 代码。以下是一个 vite.config.ts 配置的示例,我会仔细分析:

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'
import dts from 'vite-plugin-dts'

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue(), tailwindcss(), dts()],
  build: {
    lib: {
      entry: './src/index.ts',
      name: 'VueWindow',
      fileName: 'index',
      formats: ['es', 'cjs'],
    },
    rollupOptions: {
      external: ['vue', 'pinia'],
      output: {
        globals: {
          vue: 'Vue',
          pinia: 'Pinia',
        },
      },
    },
    cssCodeSplit: true, // 必须保留!
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
})

🔌 plugins 插件配置

plugins: [vue(), tailwindcss(), dts()]
  • vue():启用对 Vue 单文件组件 (.vue) 的支持。
  • tailwindcss():集成 Tailwind CSS,如果组件用到了 Tailwind 样式,这个插件很必要。
  • dts():使用 vite-plugin-dts 自动生成 .d.ts 类型文件,配合 tsconfig.json,这一步非常关键 ✅。

🏗️ build.lib 构建配置(核心部分)

build: {
  lib: {
    entry: './src/index.ts',      // 入口文件(是你库的“总出口”)
    name: 'VueWindow',            // UMD 模式下全局变量名(一般用不到)
    fileName: 'index',            // 输出文件名(会变成 index.[format].js)
    formats: ['es', 'cjs'],       // 输出格式:ESModule 和 CommonJS 两种
  },
  • 这段配置告诉 Vite,我们要把这个项目打包成一个库,而不是 SPA 页面。
  • 它会生成两个版本的构建结果(ESM 和 CJS),配合 exports 字段发布到 npm。

📦 rollupOptions 外部依赖配置

rollupOptions: {
  external: ['vue', 'pinia'],
  output: {
    globals: {
      vue: 'Vue',
      pinia: 'Pinia',
    },
  },
}
  • external: 告诉打包器不要把这些库打进最终的 bundle 里,而是让使用者自己安装。这是发布 npm 包时必须做的事,否则会把 vue 等库重复打包进去,体积超大 ❌。
  • globals: 如果你将来打 UMD 包(比如给浏览器用),这能告诉它全局变量名。

🎨 cssCodeSplit 和 resolve.alias

cssCodeSplit: true,
resolve: {
  alias: {
    '@': fileURLToPath(new URL('./src', import.meta.url)),
  },
},
  • cssCodeSplit: true: 保证 CSS 被拆分出来,方便按需引入。组件库一定要开启,否则所有样式会打成一个文件。
  • alias: 方便你在代码里用 @/components/Button.vue 这样的路径,提升开发体验。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇