用 vite 构建 vue3 组件库,并发布到 npm

1. 新建项目

1
npm init vue@latest

2. 创建要构建的组件

例如我们要封装一个文件上传的组件,在 src/components 目录下新建文件夹 silence-file-upload,具体目录结构如下:

1
2
3
4
5
6
7
8
9
> components
> silence-file-upload
> src
| > components
| > utils
| > types
| index.d.ts
index.vue
main.ts
  • index.vue:存放组件的主要代码;
  • main.ts:组件的入口文件;
  • src/components:存放 index.vue 中用到的次级组件;
  • src/utils:存放一些工具类的方法/函数;
  • src/types:存放类型声明文件

编写完组件的主要代码后,在 main.ts 文件中添加如下代码:

1
2
3
4
5
6
7
8
9
import silenceFileUpload from "./index.vue";
import type { App } from "vue";


export default {
install: (app: App) => {
app.component("SilenceFileUpload", silenceFileUpload);
}
}

install 方法可以使组件在被使用 app.use() 注册后生效。

3. 本地验证

我们可以先在本地验证组件是否正常运行。
在项目根目录下的 src/main.ts 中注册组件:

1
2
3
4
5
6
7
8
import { createApp } from 'vue'
import App from './App.vue'
import SilenceFileUpload from "@/components/silence-file-upload/index";

const app = createApp(App)
app.use(SilenceFileUpload)

app.mount('#app')

运行项目之后就可以看到组件是否正常运行。

4. 构建输出

在项目的根目录的 vite.config.ts 中修改相关配置,修改成组件库打包模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { fileURLToPath, URL } from 'node:url'
import { resolve } from "path"
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'


export default defineConfig({
plugins: [
vue()
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
// 打包后输出的文件夹
outDir: "silence-file-upload",
// 库打包模式
lib: {
// 入口文件
entry: resolve(__dirname, "./src/components/silence-file-upload/main.ts"),
// name 是暴露的全局变量
name: "SilenceFileUpload",
// fileName 默认是 package.json 的 name
fileName: "silence-file-upload"
},
rollupOptions: {
// 外部化处理那些不想被打包进库的依赖
external: ["vue", "element-plus"],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: "Vue",
"element-plus": "ElememtPlus"
}
}
}
}
})

然后执行 npm run build,在根目录下出现文件夹 silence-file-upload:

1
2
3
4
> silence-file-upload
silence-file-upload.mjs
silence-file-upload.umd.js
style.css

5. 上传到 npm

5.1 准备工作
5.1.1 添加 README.md

添加一些对该组件的介绍以及使用说明。

5.1.2 添加 ts 声明文件

在打包好的 silence-file-upload 目录下新建 index.d.ts 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 添加一些开发者可能用到的接口、类型、函数、命名空间等,如
export interface Files {
fileName: string;
filePath?: string;
status?: "success" | "error" | "uploading";
key?: string;
isMounseHover?: boolean;
progress?: number;
}

declare const default_: {
install: (app: any) => void
}

export default default_;
5.1.3 配置 package.json

首先我们在打包好的 silence-file-upload 目录下,打开终端,然后执行 npm init -y 命令初始化 package.json。
然后进行如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"name": "silence-file-upload",
"version": "1.0.0",
"private": false,
"typings": "index.d.ts", // 配置声明文件
"description": "文件上传组件",
"main": "silence-file-upload.umd.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
// 在 npm 上可搜索的关键字
"keywords": [
"upload",
"fileUpload",
"file",
"file-upload"
],
"author": "",
"license": "ISC"
}
5.2 上传到 npm
5.2.1 注册 npm 账号

想要发布到 npm 仓库,就必须要有一个账号,先去 npm 官网注册一个账号,注意记住用户名、密码和邮箱,发布的时候会用到。

5.2.2 设置 npm 源

在国内很多小伙伴喜欢把本地的 npm 镜像源采用的是淘宝镜像源或者其它的,如果想要发布 npm 包,我们得把我们的 npm 源切换为官方得源,命令如下:

1
npm config set registry=https://registry.npmjs.org
5.2.3 登录

在打包后的文件根目录打开终端,输入一下指令登录到官网:

1
npm login

会提示你输入账号密码。此外,还会给你邮箱发送一个一次性密码让你输入。

5.2.4 推送到 npm 仓库
1
npm publish

注意:发布前要保证没有相同的包名,否则无法发布,每次发布的版本号必须不同。
当修改了一些东西再次发布的时候希望版本号自动 +1,可以执行:

1
2
npm version patch
npm publish

之后,你就可以在 npm 官网自己的仓库中看到了。

6. 下载与使用

安装
1
npm i silence-file-upload

注意:这里仍然要用 npm 官方源。
然后,就可以在 node_modules 中找到我们的库了。

引入

在 main.ts 中添加:

1
2
3
4
import SilenceFileUpload from "silence-file-upload";
import "silence-file-upload/style.css";

app.use(SilenceFileUpload);
使用
1
<silence-file-upload v-model="fileList" />

拓展

在库模式下,如果我们的代码中引入了外部依赖(包括 CDN 链接),那么这些依赖在打包时会被编译进来。

如果我们想减小包的体积,不想将这些依赖打包进来,就需要用到 rollup 的 external 属性。该属性可以将匹配到的模块进行外部化处理,那什么是外部化处理呢?

比如我们有以下代码:

1
2
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";

这里“vue” 和 “element-plus”就是依赖,如果我们直接打包,打包后的代码中就不会出现

1
2
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";

而是被直接编译了进来,这也意味着当人们使用它时,不需要额外安装“vue” 和 “element-plus”。但是这样做的后果就是打包后的代码可能会有成千上万行,影响性能。

如果进行了外部化处理,你可以在打包后的代码中看到类似这样的内容:

1
2
import xxx from "vue";
import xxx from "element-plus";

“vue” 和 “element-plus”被从外部导入了,他们没有被编译进来,这时包的体积可能只有几百行代码。但是在使用这个库时,需要额外安装这两个依赖。

想要在安装库时自动安装该库所需要的依赖,可以在发布前在 package.json 中配置 dependencies

1
2
3
4
"dependencies": {
"vue": "^3.2.47",
"element-plus": "^2.3.4"
}
devDependencies 和 dependencies

npm 包声明会添加到 dependencies 或者 devDependencies 中。dependencies 中声明的是项目在生产环境中所必需的包。devDependencies 中声明的是开发阶段需要的包,如 Webpack、ESLint、Babel 等,用来辅助开发。打包上线时并不需要这些包,所以要根据包的实际用途把它们声明在适当的位置。