构建并发布 npm 库
工具库
不兼容库
不兼容指不同时兼容 CommonJS 和 ESM,只能在其中一种模式下运行。
js 库
新创建一个工作目录,执行 npm init -y
。修改 package.json
文件,将 type
设置为 mudule
或 commonjs
。根据已选择的模块系统进行开发,开发完之后配置 package.json
,之后便可直接发布。这种情况适用于开源的较小的库,可以不打包直接发布源码。
ts 库
需要在上述基础上安装 typescript
:
1 | npm i typescript -D |
然后执行 npx tsc --init
,会初始化一个 ts.config.json
文件,修改配置文件如下:
1 | { |
include
指定要编译的 ts 文件的路径,我们一般将业务代码放在 src
目录中。 outDir
指定了存放编译后的 js 文件的目录。若想在 ts 编译时自动生成 .d.ts
文件,增加以下配置:
1 | { |
生成的 .d.ts
文件也存放在 outDir
指定的目录中。最后,编译时,只需要执行
1 | tsc |
即可。
兼容库
要想兼容 ESM 和 CommonJS,我们需要借助构建工具构建代码。
unbuild
unbuild 是一个轻量级且高效的构建工具,特别适合用于打包 JavaScript/TypeScript 库。它基于 esbuild 提供了快速的构建速度,并且通过简单的配置可以生成多种格式的输出文件(如 ESM、CJS 和 UMD)。
我们在上面 ts 库的基础上安装 unbuild
:
1 | npm i unbuild -D |
由于 unbuild 原生支持 ts,它会自动将 ts 文件编译为 js 文件,因此 tsc
命令只需要进行类型检查,不需要生成 js 文件,向 ts.config.json
添加以下配置:
1 | { |
然后在 package.json
中添加命令:
1 | { |
unbuild 默认读取 build.config.ts
或 build.config.js
配置文件。
因此在项目根目录下新建 build.config.ts
文件,添加如下内容:
1 | import { defineBuildConfig } from "unbuild"; |
然后执行
1 | npm run build |
会生成 dist
目录,里面包含 index.cjs
、index.mjs
、index.d.ts
文件。
发布
配置 package.json
需要配置的项包括:
-
name
:发布到 npm 上的库名 -
main
:CommonJS 环境下的入口文件,相对路径 -
module
:ESM 环境下的入口文件,相对路径 -
types
:类型声明文件,相对路径 -
exports
:可以定义模块的入口点(替代main
),并且可以控制如何导出模块 -
keywords
:关键字,用于在 npm 仓库搜索 -
files
: 指定将哪些文件发布到 npm 仓库
以下是一个示例:
1 | { |
上传到 npm 仓库
注册 npm 账号
想要发布到 npm 仓库,就必须要有一个账号,先去 npm 官网注册一个账号,注意记住用户名、密码和邮箱,发布的时候会用到。
设置 npm 源
在国内很多小伙伴喜欢把本地的 npm 镜像源采用的是淘宝镜像源或者其它的,如果想要发布 npm 包,我们得把我们的 npm 源切换为官方得源,命令如下:
1 | npm config set registry=https://registry.npmjs.org |
或者执行命令时指定仓库源为官方源。
登录
在打包后的文件根目录打开终端,输入一下指令登录到官网:
1 | npm login --registry=https://registry.npmjs.org |
会提示你输入账号密码。此外,还会给你邮箱发送一个一次性密码让你输入。
推送到 npm 仓库
1 | npm publish --registry=https://registry.npmjs.org |
注意:发布前要保证没有相同的包名,否则无法发布,每次发布的版本号必须不同。
当修改了一些东西再次发布的时候希望版本号自动 +1,可以执行:
1 | npm version patch |
之后,你就可以在 npm 官网自己的仓库中看到了。
组件库
1. 新建项目
1 | npm init vue@latest |
2. 创建要构建的组件
例如我们要封装一个文件上传的组件,在 src/components
目录下新建文件夹 silence-file-upload
,具体目录结构如下:
1 | > components |
- index.vue:存放组件的主要代码;
- main.ts:组件的入口文件;
- src/components:存放 index.vue 中用到的次级组件;
- src/utils:存放一些工具类的方法/函数;
- src/types:存放类型声明文件
编写完组件的主要代码后,在 main.ts 文件中添加如下代码:
1 | import silenceFileUpload from "./index.vue"; |
install
方法可以使组件在被使用 app.use()
注册后生效。
3. 本地验证
我们可以先在本地验证组件是否正常运行。
在项目根目录下的 src/main.ts
中注册组件:
1 | import { createApp } from 'vue' |
运行项目之后就可以看到组件是否正常运行。
4. 构建输出
在项目的根目录的 vite.config.ts 中修改相关配置,修改成组件库打包模式。
1 | import { fileURLToPath, URL } from 'node:url' |
然后执行 npm run build
,在根目录下出现文件夹 silence-file-upload:
1 | > silence-file-upload |
5. 上传到 npm
5.1 准备工作
5.1.1 添加 README.md
添加一些对该组件的介绍以及使用说明。
5.1.2 添加 ts 声明文件
在打包好的 silence-file-upload 目录下新建 index.d.ts 文件:
1 | // 添加一些开发者可能用到的接口、类型、函数、命名空间等,如 |
5.1.3 配置 package.json
首先我们在打包好的 silence-file-upload 目录下,打开终端,然后执行 npm init -y
命令初始化 package.json。
然后进行如下配置:
1 | { |
5.2 上传到 npm
同上。
6. 下载与使用
安装
1 | npm i silence-file-upload |
注意:这里仍然要用 npm 官方源。
然后,就可以在 node_modules 中找到我们的库了。
引入
在 main.ts 中添加:
1 | import SilenceFileUpload from "silence-file-upload"; |
使用
1 | <silence-file-upload v-model="fileList" /> |
拓展
在库模式下,如果我们的代码中引入了外部依赖(包括 CDN 链接),那么这些依赖在打包时会被编译进来。
如果我们想减小包的体积,不想将这些依赖打包进来,就需要用到 rollup 的 external 属性。该属性可以将匹配到的模块进行外部化处理,那什么是外部化处理呢?
比如我们有以下代码:
1 | import { ref, watch } from "vue"; |
这里“vue” 和 “element-plus”就是依赖,如果我们直接打包,打包后的代码中就不会出现
1 | import { ref, watch } from "vue"; |
而是被直接编译了进来,这也意味着当人们使用它时,不需要额外安装“vue” 和 “element-plus”。但是这样做的后果就是打包后的代码可能会有成千上万行,影响性能。
如果进行了外部化处理,你可以在打包后的代码中看到类似这样的内容:
1 | import xxx from "vue"; |
“vue” 和 “element-plus”被从外部导入了,他们没有被编译进来,这时包的体积可能只有几百行代码。但是在使用这个库时,需要额外安装这两个依赖。
想要在安装库时自动安装该库所需要的依赖,可以在发布前在 package.json 中配置 dependencies
:
1 | "dependencies": { |
devDependencies 和 dependencies
npm 包声明会添加到 dependencies 或者 devDependencies 中。dependencies 中声明的是项目在生产环境中所必需的包。devDependencies 中声明的是开发阶段需要的包,如 Webpack、ESLint、Babel 等,用来辅助开发。打包上线时并不需要这些包,所以要根据包的实际用途把它们声明在适当的位置。