Vue2 + Vue CLI 打包成 NPM 包(支持组件 + 工具类)
·
Vue2 + Vue CLI 打包成 NPM 包(支持组件 + 工具类)
本指南将带你从零开始,使用 Vue CLI 打包一个 支持组件 + 工具类 的 npm 包,并提供完整的打包脚本、配置文件、版本限制提示等。
这里是用 zsduan-ui 做的一个示例 你可以把 zsduan-ui 替换成自己npm包的名称
前期工作
用vue cli 创建项目 请选用vue2 ts 版本
Q:为什么用ts?A:因为你不确定创建的npm包是否会用到ts 所以选择ts
vue create vue-npm-demo
cd vue-npm-demo
npm i
npm run serve
打包npm包组件
1. 创建组件
创建组件文件 src/components/dzs-button.vue
<!-- index.vue -->
<template>
<div>
<button class="dzs-button" @click="handleClick">{{text}}</button>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component({
name : 'dzs-button'
})
export default class dzsButton extends Vue {
@Prop({
type: String,
default: ''
})
text!: string
handleClick(event: Event) {
this.$emit('click', event)
}
}
</script>
// index.ts
import dzsButton from "./index.vue";
export declare type dzsDialogInstance = InstanceType<typeof dzsButton>
export default dzsButton;
export const dzsDialogPlugin = {
install : <T extends Record<string, any>>(Vue : any , options ?: T) =>{
Vue.component(dzsButton.name, dzsButton)
},
version : '20250725'
}
export { dzsButton }
// dzs-button.d.ts
export declare class DzsButton {
/** 按钮文本*/
text?: ButtonType;
}
/**
* 点击事件
* @param event 原生事件对象
*/
interface DzsButtonEmits {
(event: 'click', e: Event): void;
}
// 导出类型供外部使用
export type { ButtonType, ButtonSize, NativeType, DzsButtonProps, DzsButtonEmits };
目录结构
my-component-library/
└── src/
└── components/
├── dzs-button.d.ts
├── index.ts
└── index.vue
2.修改 tsconfig.json 文件
新增下面的配置项
"compilerOptions": {
// ... 其他配置项
"declaration": true,
"declarationDir": "./dist/zsduan-ui/types",
"emitDeclarationOnly": true,
"outDir": "./dist/zsduan-ui",
// ... 其他配置项
}
3.修改 vue.config.js 文件
新增下面的配置项
const { defineConfig } = require('@vue/cli-service')
const path = require('path')
module.exports = defineConfig({
// ... 其他配置项
outputDir: path.resolve(__dirname, 'dist/zsduan-ui/lib'),
productionSourceMap: false,
})
4.修改 package.json 文件
新增下面的配置项
{
// ... 其他配置项
"types": "types/index.d.ts",
"main": "lib/zsduan-ui.umd.js",
"typings": "types/index.d.ts",
"peerDependencies": {
"element-ui": "^2.0.0",
"vue": "^2.6.0"
}
// ... 其他配置项
}
5.创建 index.ts 的入口文件
Q:为什么需要导出 两个dzs-button ? A:因为在项目中,我们可能会使用多个组件,所以需要导出多个组件
Q:为什么要有 checkDependencies 这个函数 ? A:为了检查依赖项是否符合
// src/index.ts
import dzsButton from './components/dzs-button'
export { dzsButton } from "./components/dzs-button"
function checkDependencies() {
try {
const elementUI = require('element-ui')
const version = elementUI.version
if (!version.startsWith('2.')) {
console.warn(
'[zsduan-ui] 检测到你安装了 element-ui@%s,我们推荐使用 element-ui@2.x 版本以确保兼容性。',
version
)
}
} catch (e) {
console.error(
'[zsduan-ui] 缺少依赖:未找到 element-ui。请运行:npm install element-ui@^2'
)
}
try {
const Vue = require('vue');
const version = Vue.version
if (!version.startsWith('2.')) {
console.warn(
'[zsduan-ui] 检测到你安装了vue@%s,我们推荐使用 vue@2.x 版本以确保兼容性。',
version
)
}
}catch (e){
console.error(
'[zsduan-ui] 缺少依赖:未找到 vue。请运行:npm install vue@^2'
)
}
}
if (process.env.NODE_ENV !== 'production') {
checkDependencies()
}
// 默认导出插件
export default {
install(Vue: any) {
Vue.component('DzsButton', dzsButton)
}
}
6. 创建 scripts/postbuild.js 打包文件
用于对组件的打包
// scripts/postbuild.js
const fs = require('fs')
const path = require('path')
const distTypesDir = path.resolve(__dirname, '../dist/zsduan-ui/types')
const srcComponentsDir = path.resolve(__dirname, '../src/components')
if (!fs.existsSync(distTypesDir)) {
fs.mkdirSync(distTypesDir, { recursive: true })
}
function copyDtsFiles() {
const dirs = fs.readdirSync(srcComponentsDir)
dirs.forEach(dir => {
const dtsFile = path.resolve(srcComponentsDir, dir, `${dir}.d.ts`)
if (fs.existsSync(dtsFile)) {
const destFile = path.resolve(distTypesDir, `${dir}.d.ts`)
// 读取原始 .d.ts 文件内容
const originalContent = fs.readFileSync(dtsFile, 'utf-8')
// 生成要追加的内容
const componentName = dir
.split('-')
.map((s, i) => (i === 0 ? s : s[0].toUpperCase() + s.slice(1)))
.join('')
const newContent = `
import { defineComponent } from 'vue'
export const ${componentName}: typeof defineComponent
`
// 拼接原始内容 + 新内容
const finalContent = originalContent + newContent
// 写入目标文件(覆盖一次)
fs.writeFileSync(destFile, finalContent, 'utf-8')
}
})
}
// 生成 index.d.ts
function generateIndexDts() {
const dirs = fs.readdirSync(srcComponentsDir)
let content = ''
dirs.forEach(dir => {
const dtsFile = path.resolve(srcComponentsDir, dir, `${dir}.d.ts`)
if (fs.existsSync(dtsFile)) {
const componentName = dir
.split('-')
.map((s, i) => (i === 0 ? s : s[0].toUpperCase() + s.slice(1)))
.join('')
content += `export { ${componentName} } from './${dir}.d.ts'\n`
}
})
fs.writeFileSync(path.resolve(distTypesDir, 'index.d.ts'), content)
}
copyDtsFiles()
generateIndexDts()
7.创建 scripts/copy-package-json.js 文件
用于对 package.json 文件进行复制
// scripts/copy-package-json.js
const fs = require('fs')
const path = require('path')
const rootPkg = require('../package.json')
const distDir = path.resolve(__dirname, '../dist/zsduan-ui')
// 解构时避免使用 `module` 这样的保留字
const {
name,
version,
main,
module: esModule,
types,
peerDependencies,
dependencies,
devDependencies,
description,
author,
license
} = rootPkg
const distPkg = {
name,
version,
main,
module: esModule,
types,
peerDependencies,
dependencies: {},
description,
author,
license
}
// 如果有 peerDependencies,保留;没有就删除这个字段
if (!distPkg.peerDependencies) {
delete distPkg.peerDependencies
}
// 写入 dist/zsduan-ui/package.json
fs.writeFileSync(
path.resolve(distDir, 'package.json'),
JSON.stringify(distPkg, null, 2),
'utf-8'
)
console.log('✅ package.json 已复制到 dist/zsduan-ui/')
// ✅ 删除 dist/zsduan-ui/types/src 目录
const srcDirInTypes = path.resolve(distDir, 'types/src')
if (fs.existsSync(srcDirInTypes)) {
fs.rmSync(srcDirInTypes, { recursive: true, force: true })
console.log('✅ 已删除 dist/zsduan-ui/types/src 目录')
} else {
console.log('ℹ️ dist/zsduan-ui/types/src 不存在,跳过删除')
}
8.在package.json 中添加 scripts
{
// ... 其他配置项
"scripts": {
// ... 其他配置项
"build:lib": "vue-cli-service build --target lib --name zsduan-ui src/index.ts && tsc && node scripts/postbuild.js && node scripts/copy-package-json.js"
// ... 其他配置项
}
// ... 其他配置项
}
打包完成后的目录结构
dist/
dist/
└── zsduan-ui/
├── lib/
| ├── demo.html
| ├── zsduan-ui.common.js
| ├── zsduan-ui.css
| ├── zsduan-ui.udm.js
| └── zsduan-ui.umd.min.js
├── types/
| ├── dzs-button.d.ts
| └── index.d.ts
└── package.json
9. 如何使用
这里没有发布到npm,请自行复制代码到 node_modules/ 中 ,当然你也可以发布到 npm
9.1 全局引用
// main.js / main.ts
import zsduanUI from 'zsduan-ui'
Vue.use(zsduanUI)
9.2 全局注册组件
import {dzsButton} from 'zsduan-ui'
Vue.component('dzs-button',dzsButton)
9.3 局部引用
import {dzsButton} from 'zsduan-ui'
export default {
components: {
dzsButton
}
}
到这里 组件已经打包完成
utils 工具类打包
1. 创建工具TS
// utils/sum.ts
type Sum = (a: number, b: number) => number
export const sum: Sum = (a, b) => {
return a + b
}
2. 创建入口文件
// utils/index.ts
export { default as sum } from './sum'
目录结构
src/
└── utils/
├── index.ts
└── sum.ts
3. 安装打包依赖
npm install --save-dev rollup @rollup/plugin-typescript @rollup/plugin-commonjs @rollup/plugin-node-resolve
4.创建 tsconfig.utils.json 放在根目录下
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declarationDir": "./dist/zsduan-ui/utils/types",
"outDir": "./dist/zsduan-ui/utils/types",
"declaration": true,
"emitDeclarationOnly": true,
"rootDir": "./src/utils",
"module": "ESNext",
"target": "ES5",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/utils/**/*"]
}
5.建 scripts/utilsbuild.js 文件
用于 生成 utils 的 types
// scripts/utilsbuild.js
const fs = require('fs-extra')
const path = require('path')
// 解析路径
const utilsSrc = path.resolve(__dirname, '../src/utils') // 源码 utils 目录
const utilsDist = path.resolve(__dirname, '../dist/zsduan-ui/utils')
const typesDir = path.join(utilsDist, 'types')
async function postBuild() {
try {
// 确保 types 目录存在
await fs.ensureDir(typesDir)
// 读取源 utils 目录下的所有 .ts(x) 文件(排除 index.ts、test 文件等)
const files = await fs.readdir(utilsSrc)
const utilFiles = files
.filter(file => {
const ext = path.extname(file)
const name = path.basename(file, ext)
// 只处理 .ts 或 .tsx 文件,且不是 index、type、d.ts 等
return (ext === '.ts' || ext === '.tsx') && !['index', 'types'].includes(name)
})
.map(file => path.basename(file, path.extname(file))) // 提取文件名(不含扩展名)
// 生成 index.d.ts 内容:动态导入所有 default export
const indexDtsContent = utilFiles
.map(name => `export { default as ${name} } from './types/${name}'`)
.join('\n') + '\n'
// 写入 index.d.ts
await fs.writeFile(path.join(utilsDist, 'index.d.ts'), indexDtsContent)
console.log('✅ index.d.ts 已生成,自动导出以下模块:', utilFiles)
} catch (err) {
console.error('❌ 类型文件处理失败:', err)
process.exit(1) // 构建失败退出
}
}
postBuild()
6.建 scripts/rollup-config-utils.js 文件
把 TS 文件编译成 JS 文件
// scripts/rollup-config-utils.js
import typescript from '@rollup/plugin-typescript'
import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'
const path = require('path')
export default {
input: 'src/utils/index.ts',
output: {
dir: 'dist/zsduan-ui/utils', // ✅ 主输出目录
format: 'esm',
sourcemap: false
},
plugins: [
resolve(),
commonjs(),
typescript({
declaration: false, // ✅ 不生成声明文件 因为我们用 tsc 进行了声明文件生成
declarationDir: path.resolve(__dirname, '../dist/zsduan-ui/utils/types'),
outDir: path.resolve(__dirname, '../dist/zsduan-ui/utils/types'),
emitDeclarationOnly: true // ✅ 只生成类型文件
})
]
}
7.建 scripts/create-utils-pkg.js 文件
生成 package.json 文件
// scripts/create-utils-pkg.js
const fs = require('fs-extra')
const path = require('path')
// 定义 utils 输出目录
const utilsDist = path.resolve(__dirname, '../dist/zsduan-ui/utils')
// 确保目录存在
async function createPackageJson() {
try {
await fs.ensureDir(utilsDist)
const pkg = {
name: 'zsduan-ui/utils',
version: '1.0.0',
main: 'index.js',
module: 'index.js',
types: 'index.d.ts',
sideEffects: false,
description: 'Utility functions for zsduan-ui components library',
keywords: ['zsduan-ui', 'utils', 'fuzzySearch'],
author: 'zs.duan',
license: 'MIT'
}
const pkgPath = path.join(utilsDist, 'package.json')
await fs.writeJson(pkgPath, pkg, { spaces: 2 })
console.log(`✅ 已生成 package.json 到 ${pkgPath}`)
} catch (err) {
console.error('❌ 生成 package.json 失败:', err)
}
}
createPackageJson()
8. 在根目录下的 package.json 文件中添加以下内容:
{
// ... 其他配置项
"scripts": {
// ... 其他配置项
"build:utils": "tsc -p tsconfig.utils.json && rollup -c scripts/rollup-config-utils.js --bundleConfigAsCjs && node scripts/utilsbuild.js && node scripts/create-utils-pkg.js"
// ... 其他配置项
}
// ... 其他配置项
}
9.打包完成后的目录结构
dist/
└── zsduan-ui/
├── utils/
├── types/
| ├── index.d.ts
| └── sum.d.ts
├── index.d.ts
├── index.js
└── package.json
10.使用方法
import { sum } from 'zsduan-ui/utils';
console.log(sum(1, 2));
到这里就完成了一个 npm 包的创建,你可以根据自己的需求进行扩展。
优化
1. 可以联合起来一起打包 在 跟根目录下 package.json 文件 增加以下脚本
{
// ... 其他配置项
"scripts": {
// ... 其他配置项
"build:npm" : "npm run build:lib && npm run build:utils"
// ... 其他配置项
}
// ... 其他配置项
}
2.整体目录结构
dist/
└── zsduan-ui/
├── lib/
| ├── demo.html
| ├── zsduan-ui.common.js
| ├── zsduan-ui.css
| ├── zsduan-ui.udm.js
| └── zsduan-ui.umd.min.js
├── types/
| ├── dzs-button.d.ts
| └── index.d.ts
├── utils/
| ├── types/
| | ├── index.d.ts
| | └── sum.d.ts
| ├── index.d.ts
| ├── index.js
| └── package.json
└── package.json
更多推荐

所有评论(0)