本文将介绍通过 rollup, 从零开始构建一个简易的可发布的npm包。本文可实现的目标如下:
- 通过 rollup进行构建
- 支持 Typescript
- 支持 npm 方式安装
- 支持 cdn 方式,在页面中引入
- 支持本地调试
- 可发布到npm
一、从 package 开始项目分析
首先,在终端中 创建我们的包名【wujs】,可执行以下命令行:
mkdir wujs
其次,将 项目【wujs】在vscode中打开,打开项目终端,并执行以下命令, 创建 package.json
npm init
最后 package.json 初始如下:
最后 package.json 初始如下:
{
"name": "wujs", // 包名
"version": "1.0.0", // 版本号
"description": "", // 描述
"main": "src/index.js", // 入口文件
"files": ['dist'] // 包发布后,需要打包上传的文件列表
}
二、rollup相关插件下载和初始配置
1.下载rollup插件
npm i rollup -D
2.在根目录,创建 rollup.config.js, 初始内容如下
import path from 'path'
const resolve = (dir) => path.resolve(__dirname, dir)
export default {
input: resolve('./src/index.js'),
output: [
{
dir: resolve('dist/esm'),
format: 'esm'
}
]
}
2.1编写脚本如下:
"scripts": {
"build": "rollup -c"
},
2.2 执行npm run build 即可实现简易编译。
三、对构建输出内容的思考
也就是我们的npm包可以支持哪种方式进行使用?常见的是输出以下三种格式:
esm – 支持npm安装,在构建工具中使用。其特点是:只编译,不打包。实现按需引入
umd – 支持cdn的方式,直接在页面中引入
cjs – 在 node 环境中使用,本文暂时忽略
此外,我们的包需要支持 Typescript, 这就需要对 TS 进行编译,常见的有: Typescript、babel、esbuild。根据有关文章分析,esbuild 的编译速度比babel更快,因此我们使用 esbuild 来将 ts 编译为 js, 使用 typescript 的 tsc 来生成 .d.ts 文件
1.主要插件下载
npm i -D rollup-plugin-esbuild typescript @babel/core @babel/preset-env @rollup/plugin-babel
1.1 其他常规rollup插件
@rollup/plugin-commonjs — 识别 commonjs 模块,并转为 es 模块供rollup 处理
@rollup/plugin-json — 将 json 格式转为es 模块
@rollup/plugin-node-resolve — 简化引入路径。不配置时,引入路径必须是完整的。
npm i -D @rollup/plugin-commonjs @rollup/plugin-json @rollup/plugin-node-resolve
2.构建编译成 esm 格式
2.1在根目录,创建 rollup.esm.config.js
import json from '@rollup/plugin-json'
import nodeResolve from '@rollup/plugin-node-resolve'
import path from 'path'
const resolve = (dir) => path.resolve(__dirname, dir)
import esbuild from 'rollup-plugin-esbuild'
export default {
input: resolve('./src/index.ts'),
output: [
{
dir: resolve('dist/esm'),
format: 'esm'
}
],
plugins: [
esbuild({
target: 'es2018'
}),
nodeResolve(),
json()
],
preserveModules: true, // 只有设置为true, 才可以实现只编译,不打包
external: [‘’] // 非常重要,dependencies 中的属性名都必须列入这里,让rollup不要对其进行打包,而是作为外部依赖
}
2.2编写脚本如下,【postbuild】是【build:esm】的后置脚本,当【build:esm】执行完后会自动执行。
配置【module】,1:用户在使用该包时,可以按需引入,享受TreeShaking带来的好处;2.在配置babel插件时,可以放心屏蔽node_modules
"module": "dist/esm/index.js",
"scripts": {
"build:esm": "rollup -c rollup.esm.config.js",
"postbuild": "tsc --emitDeclarationOnly --declaration --project ts.config.json --outDir dist/esm"
},
2.3 常规 ts.config.json配置,
(详情可参考–了不起的 tsconfig.json 指南:https://blog.csdn.net/6346289/article/details/120426715)
{
"compilerOptions": {
"module": "commonjs", // 指定生成代码的模板标准
"noImplicitAny": true, // 不允许隐式的 any 类型
"removeComments": true, // 删除注释
"preserveConstEnums": true, // 保留 const 和 enum 声明
"sourceMap": true // 生成目标文件的sourceMap文件
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
2.4 执行以下脚步,即可实现esm格式的编译
npm run build:esm
3.构建编译成 umd 格式
3.1 在根目录,创建 rollup.umd.config.js
import path from 'path'
const resolve = () => path.resolve(__dirname, dir)
import esbuild from 'rollup-plugin-esbuild'
import babel from '@rollup/plugin-babel'
import json from '@rollup/plugin-json'
import nodeResolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
export default {
input: resolve('./src/index.ts'),
output: [
{
file: resolve('dist/umd/index.js'),
format: 'umd',
name: 'wujs', // 重要,这是通过 cdn 引入后,挂载到window上的属性名
},
],
plugins: [
esbuild({
target: 'es2015' // 兼容性
}),
nodeResolve(),
json(),
commonjs(),
babel({
extensions: [".js", ".ts"],
presets: ['@babel/preset-env'],
exclude: 'node_modules/**',
babelHelpers: 'bundled'
})
]
}
3.2 编写脚本, 执行以下命令, 完成umd格式的编译
npm run build:umd
"scripts": {
"build:umd": "rollup -c rollup.umd.config.js"
},
四、对构建物进行调试
4.1 对 umd 构建物的调试
需要下载以下三个插件
cross-env — 区分开发与生产环境
rollup-plugin-serve — 启动一个http 静态服务器
rollup-plugin-livereload —监听某个文件夹,当其中的文件发生变化时,刷新页面
npm i -D cross-env rollup-plugin-serve rollup-plugin-livereload
4.1.1 修改 rollup.umd.config.js
import path from 'path'
const resolve = (dir) => path.resolve(__dirname, dir)
import esbuild from 'rollup-plugin-esbuild'
import babel from '@rollup/plugin-babel'
import json from '@rollup/plugin-json'
import nodeResolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'
const isPro = process.env.NODE_ENV === 'production'
const envPlugins = isPro ? [] : [serve({
port: 1001,
open: true,
openPage: '/base/',
contentBase: ['dist', 'examples']
}), livereload('dist/umd')]
export default {
input: resolve('./src/index.ts'),
output: [
{
file: resolve('dist/umd/index.js'),
format: 'umd',
name: 'wujs', // 重要,这是通过 cdn 引入后,挂载到window上的属性名
},
],
plugins: [
esbuild({
target: 'es2015'
}),
babel({
presets: ['@babel/preset-env'],
exclude: 'node_modules/**',
babelHelpers: 'bundled'
}),
nodeResolve(),
json(),
commonjs(),
...envPlugins
]
}
新增变化内容如下图红色框处:
4.1.2 修改脚本,区分环境。
配置 -w 或 watch ,可监听文件更新,实时刷新页面
"scripts": {
"dev:umd": "cross-env NODE_ENV=development rollup -w -c rollup.umd.config.js",
"build:umd": "cross-env NODE_ENV=production rollup -c rollup.umd.config.js",
"build:esm": "cross-env NODE_ENV=production rollup -c rollup.esm.config.js",
},
4.1.3 在根目录创建 examples/base/index.html
4.1.4 执行以下命令, 即可进行调试
npm run dev:umd
4.2 对 esm 构建物的调试
这里使用vue进行调试,如果还没有安装过,请先安装
yarn global add @vue/cli
或者:
npm install -g @vue/cli
4.2.1 创建本地调试项目
vue create example
4.2.2 安装 npm-run-all ,以便异步执行多个脚本
npm i -D npm-run-all
4.2.3 编写脚本如下,只需执行 npm run dev:esm 即可
"scripts": {
"build": "run-p \"build:*\"",
"dev:esm": "run-p watch:esm serve:vue",
"watch:esm": "cross-env NODE_ENV=development rollup -w -c rollup.esm.config.js",
"serve:vue": "cd ./examples/vuetest && yarn && yarn serve",
},
4.2.4 在vue项目中调试,在引入时,其路径无需指定到具体文件,会自动找pkg.main、pkg.module进行匹配
4.2.5 如果报eslint配置错误,可以在vue3项目中添加vue.config.js ,并设置
module.exports = {
lintOnSave: false
}
4.2.6 如果报引入路径错误问题。
首先,请先检查,是否将pkg.mian 和 pkg.module指向相关输出文件,如下图(这个的前提是相关配置文件正确)。其次是,引入路径是否可以找到的输出目录,如dist
五、发布到NPM上
首先要有npm的账号,没有的话,先注册一个。
5.1 执行,会进行登录
npm login
[图片]
5.2 执行以下命令 ,实现包版本号加1
npm version patch
5.3 执行执行以下命令 ,发布到npm
npm publish
如果现在的版本号跟已经发布的重复,则会报错,需要重新执行 【5.2】和【5.3】的步骤,见下图:
至此,完成项目的构建与发布!
附上终极版 package.json 文件如下:
{
"name": "wujs",
"version": "1.1.0",
"description": "",
"main": "dist/umd/index.js",
"module": "dist/esm/index.js",
"files": [
"dist"
],
"scripts": {
"说明1": "--------------------调试umd格式:执行下面这条脚本↓---------------------",
"dev:umd": "cross-env NODE_ENV=development rollup -w -c rollup.umd.config.js",
"说明2": "-----------------在vue3中调试esm格式:执行下面这条脚本↓-----------------",
"dev:esm": "run-p watch:esm serve:vue3",
"watch:esm": "cross-env NODE_ENV=development rollup -w -c rollup.esm.config.js",
"serve:vue3": "cd ./examples/vuetest && yarn && yarn serve",
"说明3": "--------------------打包构建:执行下面这条脚本↓---------------------",
"build": "run-p \"build:*\"",
"build:umd": "cross-env NODE_ENV=production rollup -c rollup.umd.config.js",
"build:esm": "cross-env NODE_ENV=production rollup -c rollup.esm.config.js",
"postbuild:esm": "tsc --emitDeclarationOnly --declaration --project tsconfig.json --outDir dist/esm"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@rollup/plugin-babel": "^5.3.1",
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"cross-env": "^7.0.3",
"npm": "^8.17.0",
"npm-run-all": "^4.1.5",
"rollup": "^2.78.0",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-esbuild": "^4.9.3",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^2.0.1",
"typescript": "^4.7.4"
}
}