这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战
Rollup是一个JavaScript的模块化打包工具,可以帮助我们编译小的代码到一个大的、复杂的代码中,比如一个库或者一个应用程序
Rollup的定义、定位和webpack非常的相似
-
Rollup也是一个模块化的打包工具,但是Rollup主要是针对ES Module进行打包的
默认情况下rollup是无法对其它模块进行打包操作的,
如果要对其它模块方式编写的模块进行打包,需要依赖对应的插件
-
webpack通常可以通过各种loader处理各种各样的文件,以及处理它们的依赖关系
rollup更多时候是专注于处理JavaScript代码的(当然也可以处理css、font、vue等文件)
-
rollup的配置和理念相对于webpack来说,更加的简洁和容易理解
-
在早期webpack不支持tree shaking时,rollup具备更强的优势
-
在实际项目开发过程中,我们都会使用webpack(比如vue、react、angular项目都是基于webpack的)
在对库文件进行打包时,我们通常会使用rollup(比如vue、react、dayjs源码本身都是基于rollup的);
使用
命令行使用
安装
# 全局安装
npm install rollup -g # 局部安装
npm install rollup -D
使用
# 打包浏览器的库
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) --- UMD的库可以兼容AMD,CJS和浏览器
# 在兼容浏览器的时候需要将打包后的内容挂载到全局对象window上,所以需要指定挂载到window上的那个属性的名称(name)
# 类似于dayjs中的dayjs,lodash中的_
npx rollup ./src/main.js -f umd --name mathUtil -o dist/bundle.js
配置文件
我们可以将配置信息写到配置文件中rollup.config.js文件:
rollup.config.js
// 可以使用module.exports导出
// 但是rollup默认是对ESM进行打包的,所以多数情况下使用ESM的导出方式
export default {
// 入口
input: './src/main.js',
// 出口
output: {
// 输出的文件需要在那个平台上运行
format: 'umd',
// 是umd就需要提供name
name: 'main',
// 输出到哪里
file: './dist/main.umd.js'
}
}
package.json
"scripts": {
// rollup -c rollup.config.js 可以简写为 rollup -c 因为rollup.config.js是默认的配置文件
// -c后可以跟自定义的配置文件 ---> rollup -c rollup.custom.config.js
"build": "rollup -c"
}
可以对文件进行分别打包,打包出更多的库文件(用户可以根据不同的需求来引入)
export default {
input: './src/main.js',
// output的值可以设置为数组,从而打包出多个运行在不同平台上的文件
output: [
{
format: 'umd',
name: 'main',
file: './dist/main.umd.js'
},
{
format: 'amd',
file: './dist/main.amd.js'
},
{
format: 'cjs',
file: './dist/main.cjs.js'
},
{
format: 'es',
file: './dist/main.es.js'
},
{
format: 'iife',
// 在浏览器端使用的时候,推荐将打包后的内容挂载到全局变量上,以便于调用者进行使用
name: 'main',
file: './dist/main.js'
}
]
}
插件使用
rollup和别的构建工具一样可以使用对应的插件来扩展构建工具的功能,
可以在这里查看rollup官方提供的插件
使用CJS打包的库
rollup主要是对ESM进行打包处理的,默认情况下,rollup不能使用CJS编写的模块
# 如果需要在rollup中引入cjs编写的包,需要使用插件@rollup/plugin-commonj
npm install @rollup/plugin-commonjs -D
// 和webpack不同的是 rollup的插件导出的是一个个的函数
import commonjs from '@rollup/plugin-commonjs'
export default {
input: './src/main.js',
output: {
format: 'umd',
name: 'main',
file: './dist/main.umd.js'
},
// 使用插件
plugins: [
commonjs()
]
}
cjs模块
// 这里模拟使用的第三方CJS模块
const name = 'foo'
module.exports = {
name
}
使用CJS模块
// rollup默认可以解析js,json后缀的文件,所以可以省略对应的后缀名
import { name } from './foo/index'
// 在rollup中虽然存在插件可以帮助我们将CJS模块编写的包进行解析并引入
// 但是rollup不允许我们使用CJS的方式将对应的模块进行引入 ---> 也就是说rollup仅支持CJS的导出,不支持CJS的导入方式
// 因为rollup需要基于ESM进行tree shaking操作,所以对于我们自己编写的代码,rollup强制我们使用ESM的方式进行代码的编写
// const { name } = require('./foo/index')
console.log(name)
export const sum = (num1, num2) => num1 + num2
使用node_module下的第三方库
rollup在多数情况下是用来对第三方库进行打包的构建工具,所以默认情况下,如果我们使用了第三方库的时候,代码可以正常编译
但是对应的模块并不会被引入,而是需要我们自己在html文件中使用CDN的方式进行手动引入
例如:当我们使用lodash模块的时候,( 默认情况下,lodash模块是CJS方式导出的,所以需要先使用@babel/plugin-commonjs插件进行解析 )
import _ from 'lodash'
console.log(_.join([22, 33]))
编译后
// ...
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
// 编译后的代码中并没有引入loadsh这个包
console.log(___default['default'].join([22, 33]));
// ...
所以此时直接运行编译打包后输出的main.js文件的时候,是会报错的
此时需要将html文件修改为
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 我们需要在这里手动引入对应的loadsh提供的cdn路径 -->
<script src="../node_modules/lodash/lodash.min.js"></script>
<script src="../dist/main.umd.js"></script>
</body>
</html>
但是实际这么做的时候,rollup是会报错的,因为默认情况下,rollup并无法解析node_modules下的模块,如果需要使用 ,需要安装
插件@rollup/plugin-node-resolve,这样rollup才可以正常对node_modules下的模块进行打包
import resolve from '@rollup/plugin-node-resolve'
plugins: [
resolve()
]
但是此时会发现对应的模块都被引入进来了,但是实际的时候我们并不希望引入第三方库的代码
rollup.config.js
import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'
export default {
input: './src/main.js',
output: {
format: 'umd',
name: 'main',
file: './dist/main.umd.js',
// 指定全局的_来自于lodash模块
globals: {
'lodash': '_'
}
},
// 将lodash的包从构建后的代码中抽离
external: [
'lodash'
],
plugins: [
commonjs(),
resolve()
]
}
ES6转ES5
# 安装
npm i @rollup/plugin-babel -D
import babel from '@rollup/plugin-babel'
plugins: [
babel({
exclude: 'node_modules/**'
// 在ES6转换为ES5的时候,会使用到一些helper函数
// 如果在转换的过程中,出现多个重复的helper函数,那么会将这些重复的helper函数合并成一个
babelHelpers: 'bundled'
})
]
压缩代码
# 下面这个插件是第三方插件 --- 该插件内部使用的依旧是terser,只不过提供了其在rollup中的支持
npm i rollup-plugin-terser -D
// rollup-plugin-terser的导出使用的不是默认导出方式
import { terser } from 'rollup-plugin-terser'
plugins: [
// rollup-plugin-terser提供了默认的terser配置
terser()
]
处理其它文件
css文件
和在webpack中加载非js文件一样,webpack是依赖于各种各样的loader的,而rollup则是依赖于各种各样的plugins
为了在rollup中可以加载css文件,我们需要安装插件rollup-plugin-postcss
rollup-plugin-postcss默认集成了对scss、less、stylus的支持,在我们项目中,只要配置了rollup-plugin-postcss,就可以直接使用这些css预编译器,
# 安装
npm install rollup-plugin-postcss postcss -D
rollup.config.js
import postcss from 'rollup-plugin-postcss'
plugins: [
// 插件的执行存在一定的先后顺序,所以postcss的执行应该放置在terser之前
postcss(),
terser()
]
vue文件
# 安装
npm i rollup-plugin-vue rollup-plugin-replace -D
import vue from 'rollup-plugin-vue'
import replace from 'rollup-plugin-replace'
plugins: [
// commonjs插件在引入vue3的时候可能会出现问题
// 所以需要在commonjs插件之前就解析vue的SFC文件
vue(),
commonjs(),
resolve(),
replace({
// vue在处理的过程中,存在process.env.NODE_ENV,所以需要手动设置这些全局变量的值
// rollup-plugin-replace的时候和webpack中definedPlugin插件的使用方式类似
'process.env.NODE_ENV': JSON.stringify('development'),
// 同样rollup-plugin-replace也可以用来注入全局变量
ENV: JSON.stringify('development')
}),
babel({
babelHelpers: 'bundled'
}),
postcss(),
terser()
]
本地服务
# 安装
npm i rollup-plugin-serve -D
rollup.config.js
import serve from 'rollup-plugin-serve'
plugins: [
serve({
port: 8080,
contentBase: '.' // 以什么目录作为项目根目录 --- . 表示的是当前目录
})
]
live reload
# 安装
npm i rollup-plugin-livereload -D
package.json
"scripts": {
"build": "rollup -c",
"serve": "rollup -c -w" // 监听文件变化并实时进行编译
}
rollup.config.js
import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'
plugins: [
serve({
port: 8080,
contentBase: '.'
}),
livereload()
]
区分环境
package.json
"scripts": {
"build": "rollup -c --environment NODE_ENV:production",
// --environment NODE_ENV:development配置的环境变量
// 可以在rollup.config.js中通过process.env.NODE_ENV来获取具体的值
"serve": "rollup -c -w --environment NODE_ENV:development"
}
rollup.config.js
import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'
import babel from '@rollup/plugin-babel'
import { terser } from 'rollup-plugin-terser'
import postcss from 'rollup-plugin-postcss'
import vue from 'rollup-plugin-vue'
import replace from 'rollup-plugin-replace'
import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'
// 获取传入的环境变量
const isDev = process.env.NODE_ENV === 'development'
const plugins = [
vue(),
commonjs(),
resolve(),
replace({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
ENV: JSON.stringify('development')
}),
babel({
babelHelpers: 'bundled'
}),
postcss()
]
// 根据不同的环境变量,使用不同的插件
if (isDev) {
plugins.push(...[
serve({
port: 8080,
contentBase: '.'
}),
livereload()
])
} else {
plugins.push(terser())
}
export default {
input: './src/main.js',
output: {
format: 'umd',
name: 'main',
file: './dist/main.umd.js',
globals: {
'lodash': '_'
}
},
external: [
'lodash'
],
plugins
}