库打包工具 rollup
摘要
**概念:**rollup是一个模块化的打包工具
注:实际应用中,rollup更多是一个库打包工具
与Webpack的区别:
文件处理:
- rollup 更多专注于 JS 代码,并针对 ES Module 进行打包
- webpack 通常可以通过各种 loader 处理多种文件及其对应的依赖关系
应用场景:
- 在实际项目开发中,通常使用webpack(比如react、angular项目都是基于webpack的)
- 在对库文件进行打包时,通常会使用rollup(比如vue、react、dayjs源码本身都是基于rollup的,Vite底层使用Rollup)。
概念
官方对rollup的定义:
Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application.
Rollup是一个JavaScript的模块化打包工具,可以帮助我们编译小的代码到一个大的、复杂的代码中,比如一个库或者一个应用程序;
我们会发现Rollup的定义、定位和webpack非常的相似:
- Rollup也是一个模块化的打包工具,但是Rollup主要是针对ES Module进行打包的,webpack则通常可以通过各种loader处理各种各样的文件,以及处理它们的依赖关系。
- rollup更多时候是专注于处理JavaScript代码的(当然也可以处理css、font、vue等文件)。
- rollup的配置和理念相对于webpack来说,更加的简洁和容易理解; 在早期webpack不支持tree shaking时,rollup具备更强的优势。
webpack和rollup各自的应用场景:
通常在实际项目开发中,我们都会使用webpack(比如react、angular项目都是基于webpack的)
在对库文件进行打包时,我们通常会使用rollup(比如vue、react、dayjs源码本身都是基于rollup的,Vite底层使用Rollup)。
Rollup的基本使用
安装rollup:
# 全局安装
npm install rollup -g
# 局部安装
npm install rollup -D
创建main.js文件,打包到bundle.js文件中:
参数:
-f
: format
-f cjs // commonjs,支持node环境
-f iife // 立即执行函数表达式
-f amd // amd,支持amd环境
-f umd // umd,支持通用的模块化环境
# 打包浏览器的库
npx rollup ./src/main.js -f iife -o dist/bundle.js
# 打包AMD的库
npx rollup ./src/main.js -f amd -o dist/bundle.js
# 打包CommonJS的库
npx rollup ./src/main.js -f cjs -o dist/bundle.js
# 打包通用的库(必须跟上name)
npx rollup ./src/main.js -f umd --name mathUtil -o dist/bundle.js
Case: 打包index为bundle.js
指令如下所示:
nathanchen@NathansMacBook-Pro rollup % npx rollup ./lib/index.js -f umd --name mathUtil -o dist/bundle.js
./lib/index.js → dist/bundle.js...
created dist/bundle.js in 22ms
index.js
function foo() {
console.log("foo excetion")
}
export {
foo
}
bundle.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mathUtil = {}));
})(this, (function (exports) { 'use strict';
function foo() {
console.log("foo excetion");
}
exports.foo = foo;
}));
Rollup的配置文件
我们可以将配置信息写到配置文件中rollup.config.js
文件。
module.exports = {
// 入口
input: "./lib/index.js",
// 出口
output: {
format: "umd",
name: "nathanUtils",
file: "./build/bundle.umd.js"
},
}
执行命令即可,-c
指的是使用配置文件:
npx rollup -c
我们可以对文件进行分别打包,打包出更多的库文件(用户可以根据不同的需求来引入)。
module.exports = {
// 入口
input: "./lib/index.js",
// 出口
output:
[
{
format: "umd",
name: "nathanUtils",
file: "./build/bundle.umd.js"
},
{
format: "amd",
name: "nathanUtils",
file: "./build/bundle.amd.js"
},
{
format: "cjs",
name: "nathanUtils",
file: "./build/bundle.cjs.js"
},
{
format: "iife",
name: "nathanUtils",
file: "./build/bundle.browser.js"
},
]
}
解决commonjs和第三方库问题
**问题背景:**引入第三方包,比如 Lodash,进行打包时,发现没有对 引入包 进行打包。
原因:
默认 lodash 没有被打包是因为它使用的是 commonjs,rollup默认情况下只会处理 ES Module
解决方案:
安装解决commonjs的库:
npm install @rollup/plugin-commonjs -D
安装解决node_modules的库:
npm install @rollup/plugin-node-resolve -D
npx rollup -c
进行打包即可,可以发现 Lodash 被打包进入 bundle.js
不过在实际应用中,我们一般排除打包依赖包,当用户使用时包时,让他们自己进行安装即可
rollup.config.js
module.exports = {
output:
[
{
format: "umd",
name: "nathanUtils",
file: "./build/bundle.umd.js",
globals: {
lodash: "_" // 给 lodash 全局命名为 _
}
},
],
external: ["lodash"],
}
Babel 转换代码
**需求:**如果我们希望将ES6转成ES5的代码,可以在rollup中使用babel。
安装rollup对应的babel插件:
npm install @rollup/plugin-babel -D
修改配置文件:
配置 rollup.config.js
配置 babelHelpers
,不让 Babel 处理 node_modules
// 使用代码转换和压缩
const babel = require('@rollup/plugin-babel')
module.exports = {
...
plugins: [
babel({
babelHelpers: "bundled",
exclude: /node_modules/
})
]
}
配置babel.config.js
module.exports = {
presets: ["@babel/preset-env"]
}
产物:
打包前 babel.config.js
import { sum, mul } from './utils/math'
const foo = () => {
console.log("foo excetion")
}
export {
foo,
sum,
mul
}
打包后 bundle.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mathUtil = {}));
})(this, (function (exports) { 'use strict';
function sum(num1, num2) {
return num1 + num2
}
function mul(num1, num2) {
return num1 * num2
}
function foo() {
console.log("foo excetion");
}
exports.foo = foo;
exports.mul = mul;
exports.sum = sum;
}));
Teser 代码压缩
如果我们希望对代码进行压缩,可以使用@rollup/plugin-terser
npm install @rollup/plugin-terser -D
配置terser:babel.config.js
const terser = require('@rollup/plugin-terser')
module.exports = {
...
plugins: [
terser()
]
}
打包后 bundle.js
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).nathanUtils={})}(this,(function(e){"use strict";e.foo=function(){console.log("foo excetion")},e.mul=function(e,n){return e*n},e.sum=function(e,n){return e+n}}));
处理css文件
如果我们项目中需要处理css文件,可以使用postcss:
npm install rollup-plugin-postcss postcss -D
配置postcss的插件:可以补充安装下 postcss-preset-env
,处理下样式兼容性问题
const postcss = require('rollup-plugin-postcss')
module.exports = {
...
plugins: [
postcss({ plugins: [require("postcss-preset-env")]})
]
}
处理vue文件
处理vue文件我们需要使用rollup-plugin-vue插件
注意:默认情况下我们安装的是vue2.x的版本,所以我这里指定了一下rollup-plugin-vue的版本;
npm install rollup-plugin-vue @vue/compiler-sfc -D
使用vue的插件:vue()
打包vue报错:
在我们打包vue项目后,运行会报如下的错误:
Uncaught ReferenceError: process is not defined
这是因为在我们打包的vue代码中,用到 process.env.NODE_ENV,所以我们可以使用一个插件 rollup-plugin-replace
设置它对应的值:
npm install @rollup/plugin-replace -D
配置插件信息:
replace({
'process.env.NODE_ENV': JSON.stringify('production')
})
搭建本地服务器
第一步:使用rollup-plugin-serve搭建服务
npm install rollup-plugin-serve -D
rollup.config.js
module.exports = {
...
plugins: [
...
serve({
port: 8000,
open: true,
contentBase: '.' // 服务当前文件夹
})
]
}
第二步:当文件发生变化时,自动刷新浏览器
npm install rollup-plugin-livereload -D
第三步:启动时,开启文件监听
npx rollup -c -w
效果:
区分开发环境
我们可以在 package.json
中创建一个开发和构建的脚本:
"scripts": {
"build": "rollup -c --environment NODE_ENV:production",
"serve": "rollup -c --environment NODE_ENV:development -w"
},
rollup.config.js
if (isProduction) {
plugins.push(terser())
} else {
const extraPlugins = [
serve({
port: 8000,
open: true,
contentBase: '.' // 服务当前文件夹
}),
]
plugins.push(...extraPlugins)
}