手把手教你使用Rollup构建javascript模块库
本篇会教你如何使用Rollup打包TypeScript项目,导出esm/umd模块库,在进行打包代码配置之前,稍微介绍一下Rollup,让刚接触的小伙伴知道啥是Rollup,已经学习过Rollup可以跳过前面的介绍,直接看工程实战部分~
Rollup
Rollup 是一个 JavaScript 模块打包工具,可以将多个小的代码片段编译为完整的库和应用。与传统的 CommonJS 和 AMD 这一类非标准化的解决方案不同,Rollup 使用的是 ES6 版本 Javascript 中的模块标准。新的 ES 模块可以让你自由、无缝地按需使用你最喜爱的库中那些有用的单个函数。这一特性在未来将随处可用,但 Rollup 让你现在就可以,想用就用。
配置
下面用一个简单的示例来看看Rollup 是如何工作的。首先创建Rollup的配置文件rollup.config.js及我们打包的项目文件app.js。
//rollup.config.js
module.exports = {
input: 'src/app.js',
output: {
file: 'dist/boundle.js',
format: 'cjs'
}
};
//src/app.js
console.log('My first rollup app.')
与Webpack 一般的项目内部不同,Rollup直接全局安装即可:
(sudo) npm i rollup -g
然后使用Rollup的命令行指令进行打包:
rollup -c rollup.config.js
-c 参数是告诉Rollup使用该配置文件,打包结果如下:
'use strict';
console.log('My first rollup app.')
可以看到,我们打包出来的东西很干净,Rollup并没有添加什么额外的代码(就连第1行的'use strict都可以通过配置output.strict去掉')。 同样的代码如果你用webpack打包试试,多出好多额外的代码。
配置文件说明
通常,这个配置文件位于你项目的根目录,并且命名为 rollup.config.js。Rollup 将会在配置文件被依赖之前,在后台将配置文件和 CommonJS 的相关依赖项进行转译和打包,这样做的优点就是你可以与 ES 模块代码库共享代码,同时与 Node 生态完全互通。
如果你想要使用 require 和 module.exports 将配置文件写成一个 CommonJS 模块,则应该将文件扩展名更改为 .cjs,这会阻止 Rollup 尝试转译文件。此外,如果你使用的是 Node 13+,则将文件扩展名更改为 .mjs 也可以阻止 Rollup 进行编译,并将文件导入为 ES 模块
Rollup 去除死代码
Tree shaking, 最开始是由Rollup实现的,之后被webpack借鉴了过去。 Rollup去除死代码也是基于对ES6 Module的静态分析,找出没有被引用过的模块,将其从最后生成的bundle中排除。 下面看简单的代码示例:
// app.js
import { add } from './util';
console.log(`2 + 3 = ${add(2,3)}`);
// util.js
export function add(a ,b) {
return a + b;
}
export function sub(a ,b){
return a - b;
}
Rollup 的打包结果如下:
'use strict';
function add(a, b){
return a + b;
}
console.log(`2 + 3 = ${add(2,3)}`);
可以看到,util.js中的sub函数没有被引用过,因此也没有出现在最终的bundle.js中。与之前输出的内容非常清晰简洁,没有附加代码。
可选的输出格式
Rollup有一项Webpack不具备的特性,通过配置output.format,开发者可以选择输出资源的模块形式。 上面例子中我们使用的是cjs(CommonJS),除此之外Rollup还支持amd、esm、iife、umd及system。 这项特性针对打包JavaScript库特别有用,因为往往一个库需要支持多种不同的模块形式,而通过Rollup, 用几个命令就可以把一份源代码打包为多份。下面使用一段简单的代码进行举例。
'use strict'
export function add(a ,b) {
return a + b;
}
export function sub(a ,b){
return a - b;
}
当 output.fromat 是cjs(CommonJS)时,输出如下:
Object.defineProperty(exports, '__esModule', { value: true });
export function add(a ,b) {
return a + b;
}
export function sub(a ,b){
return a - b;
}
exports.add = add;
exports.sub = sub;
当output.format是esm(ES6 Module)时,输出如下:
function add(a , b){
return a + b;
}
function sub(a, b){
return a - b;
}
export { add, sub };
工程实战
TypeScript+React18编写的工具库,要求同时输出esm/umd模块。 工程目录如下:
public/
index.html
rollup/
rollup.config.mjs
rollup.esm.config.mjs
rollup.umd.config.mjs
src/
components/
...
hooks/
...
styles/
...
index.tsx
index.css
package.json
tsconfig.json
.README.md
rollup文件夹下面分别创建 rollup.esm.config.mjs 和 rollup.umd.config.mjs 并把两个配置公共的部分抽取出来rollup.config.mjs
公共部分 config
我们看看公共部分的配置文件:
//rollup.config.mjs
import typescript from 'rollup-plugin-typescript2'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import sass from 'rollup-plugin-sass'
import autoprefixer from 'autoprefixer'
import postcss from 'postcss'
//rollup-plugin-typescript2 插件配置项目
const overrides = {
compilerOptions: { declaration: true },
exclude: ["src/**/*.test.tsx", "src/setupTests.ts"]
}
const config = {
input: 'src/index.tsx', // 入口项(必选)
plugins: [
nodeResolve(),
commonjs(),
json(),
typescript({ tsconfigOverride: overrides }),
sass({
output: 'dist/index.css',
processor: css => postcss([autoprefixer])
.process(css)
.then(result => result.css)
})
],
}
export default config
rollup生态圈也是提供了丰富的插件来处理各类业务,我们着重说一下上面用到的几个插件。
插件 @rollup/plugin-node-resolve:
npm install @rollup/plugin-node-resolve -D
@rollup/plugin-node-resolve 插件来处理外部模块(rollup默认无法处理外部模块,也就是说无法解析打包从npm上下载使用的包,使用这个插件可以帮助我们使用)。
插件 @rollup/plugin-commonjs:
npm install @rollup/plugin-commonjs -D
一些库会导出成你可以正常导入的 ES6 模块。 但是目前, npm 中的大多数包都是以 CommonJS 模块的形式出现的。 在它们更改之前,我们需要将 CommonJS 模块转换为 ES2015 供 Rollup 处理。
插件 rollup-plugin-json:
npm install rollup-plugin-json -D
使用 rollup-plugin-json 插件来处理json文件。
插件 rollup-plugin-typescript2:
npm install rollup-plugin-typescript2 -D
rollup-plugin-typescript2 支持 typescript,是原始rollup-plugin-typescript的重写。 这个版本比原来的要慢一些,但是它会打印出TypeScript的语法和语义诊断消息(毕竟这是使用TypeScript的主要原因)。
配置项可以查看文档:www.npmjs.com/package/rol…
插件 rollup-plugin-sass:
npm install rollup-plugin-sass -D
用于Rollup和Sass之间的无缝集成。
配置项可以查看文档:www.npmjs.com/package/rol…
插件 postcss:
npm install postcss -D
使用 postcss 处理css,它支持css文件的加载、css加前缀、css压缩、对scss/less的支持等等。
插件 autoprefixer:
npm install autoprefixer -D
可以使用 autoprefixer 插件为CSS3添加前缀
esm模块 config
来看构建esm库的配置文件:
// rollup.esm.config.mjs
import basicConfig from './rollup.config.mjs'
import excludeDependenciesFromBundle from "rollup-plugin-exclude-dependencies-from-bundle"
const config = {
...basicConfig, //整合公共部分
output: [
{
file: 'dist/index.es.js', //输出文件
format: 'es' //输出格式
}
],
plugins: [ //插件
...basicConfig.plugins, //整合公共部分插件
excludeDependenciesFromBundle() //忽略掉dependencies和peerDependencies的依赖
]
}
export default config
umd模块 config
导出的umd模式在浏览器中使用 script 和 link 标签直接引入文件,并使用全局变量 TEST。
来看构建umd库的配置文件:
//rollup.umd.config.mjs
import basicConfig from './rollup.config.mjs'
import { terser } from "rollup-plugin-terser"
import replace from '@rollup/plugin-replace'
const config = {
...basicConfig, //整合公共部分配置
output: [
{
name: 'TEST', //浏览器引入的全局变量名称
file: 'dist/index.umd.js', //输出文件
format: 'umd', //输出格式
exports: 'named', //导出的是全局变量命名方式
globals: { //对被排除的依赖命名
'react': 'React', //三方库映射
'react-dom': 'ReactDOM',
'axios': 'Axios'
},
plugins: [
terser() //terser插件在rollup打包过程当中实现代码压缩
],
},
],
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'), //该插件在绑定时替换文件中的目标字符串
}),
...basicConfig.plugins //整合公共部分插件
],
//有时候一些外部引用的库我们并不想一并打包在我们的库中,
//如:lodash、react,可以在配置文件中使用 external 字段来告诉rollup不要将这些库打包
external: ['react', 'react-dom', 'axios'] //排除的这些需要在页面中单独引入cdn link 链接
}
export default config
package.json 配置命令
在package.json 中配置打包命令
"scripts": {
"build-es": "rollup --config rollup/rollup.esm.config.mjs",
"build-umd": "rollup --config rollup/rollup.umd.config.mjs"
}
构建运行
在终端分别执行: npm run build-es , npm run build-umd
目标文件夹中输出:dist/index.es.js, dist/index.umd.js
发布到npmjs或者部署到服务器即可使用!
转载自:https://juejin.cn/post/7181741108697759805