如果想要在多个项目中使用同样的组件的话,每次手动导入会比较麻烦,使用 npm 包是一种便捷的做法。
不过,打包 npm 包的行为和打包为网页不太一样,我们不能够简单地直接把 build 的成果直接当做 npm 包。有几个地方需要注意,下面我把自己打包过程中的几个点总结一下。
package.json 的配置
小白需要先理解 package.json 到底是什么。package.json 是每个 npm 包的核心配置文件,它描述了包的各种信息,比如名称、版本、入口文件、依赖等。package.json 文件用于帮助 npm 理解包的内容。对于打包成 npm 包来说,package.json 中几个字段非常关键,尤其是以下几个:
"name"
:包的名称,在发布到 npm 上时不能重复。"version"
:版本号,每次发布新版本时必须更新。"main"
:指向 CommonJS 格式的入口文件,通常是打包后的index.js
或dist/index.js
。(兼容旧工具用)"module"
:指向 ES Module 格式的入口文件,很多现代打包器(如 Vite、Rollup)会优先使用它。(兼容旧工具用)"types"
:指向类型声明文件(如果你用 TypeScript 开发),例如dist/index.d.ts
。(兼容旧工具用)"exports"
:推荐使用的新字段,用于精细控制包的导出入口。
exports
是 package.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.ts
。vue-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.json
和 tsconfig.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
这样的路径,提升开发体验。