教学视频出处
https://www.bilibili.com/video/BV1w84y1z77V?p=3&spm_id_from=pageDriver&vd_source=0f7f337dd5a99bb975b88a48ae1b3711
日期:2022/12/3
rollup目前版本:
"rollup": "^3.5.1"
官网
https://rollupjs.org/guide/en/#–bundleconfigascjs
Rollup 同样也是一款 ES Modules 打包器。它也可以将项目中散落的细小模块打包为整块代码,从而使得这些划分的模块可以更好的运行在浏览器环境或者 Node.js 环境。
从作用上来看,Rollup 与 Webpack 非常类似。不过相比于 Webpack,Rollup 要小巧的多。因为 Webpack 在配合一些插件的使用下,几乎可以完成开发过程中,前端工程化的绝大多数工作。而 Rollup 可以说仅仅是一个 ESM 打包器,没有其它任何额外的功能
比如 rollup中并不支持类似HMR这种高级特性
rollup的目的不是要与webpack全面竞争,初衷是提供一个充分利用esm各项特性的高效打包器
可以先执行下面的命令得到package.json
npm init -y
我们准备一个示例demo,然后这是目录结构,里面有三个esm文件
index.js
//导入模块成员
import {log} from './logger'
import messages from "./messages";
//使用模块成员
const msg = messages.hi;
log(msg);
logger.js
export const log = msg =>{
console.log('-------INFO-----------')
console.log(msg)
console.log('----------------------')
}
export const error = msg =>{
console.log('-------ERROR-----------')
console.log(msg)
console.log('----------------------')
}
messages.js
export default {
hi: 'Hey Guys,I am zce~'
}
npm i rollup
然后我们直接运行一下 rollup
./node_modules/.bin/rollup
注意,yarn 的话 ,可以直接 如下 就能自己找到rollup
yarn rollup
从提示信息里,我们就知道可以看到它的用法,它需要提供一个入口文件
同时我们再通过 --format
指定最适合浏览器的 iife 就是自调用函数的格式
./node_modules/.bin/rollup ./src/index.js --format iife
那么此时结果就已经打印到控制台上了
我们再通过 --file
指定一个输出路径
./node_modules/.bin/rollup ./src/index.js --format iife --file dist/bundle.js
我们发现它打包结果非常简洁,输出结果它只保留用到的部分,没有用到的部分都没有输出,因为rollup会默认开启tree shaking摇树
去优化输出的结果,那么tree shaking
的概念呢,最早就是在rollup这个打包工具提出来的。
rollup也支持通过配置文件配置我们打包过程中的各项参数。在项目根目录新建 rollup.config.js
那么 rollup.config.js
这个文件也是运行在node环境中,rollup会额外的处理这个配置文件所以这里我们可以直接使用esm的写法,但是,如果要使用esm的写法,那么此时你应该在你的package.json
文件中添加上
"type": "module"
或者把后缀改成 rollup.config.cjs
,否则它会报错。
要么我们就直接使用commonjs规范导出即可(推荐)
module.exports = {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
}
}
执行命令
./node_modules/.bin/rollup --config
# 指定配置文件
./node_modules/.bin/rollup --config
rollup的功能,只是esm模块的合并和打包,那假如我们有更高级的需求,里如
1.我们要加载其它类型的资源文件
2.导入commonjs模块
3.编译ecmascript的新特性
这些额外的需求,rollup同样支持使用插件的方式实现,而且,插件时rollup的唯一拓展方式,而不像webpack它有3中拓展方式
我们这里来一个小例子,我们使用rollup-plugin-json
插件来导入json文件。
npm i rollup-plugin-json
然后在rollup.config.js
文件里引入插件。
在plugins字段,他是一个数组,里面直接调用函数
index.js文件中
执行打包
./node_modules/.bin/rollup --config rollup.config.js
打包成功后我们看看结果,它除了用到的属性外,别的属性都通过 tree shaking摇树模式给优化出去了。
rollup默认只能按照文件路径的方式去加载本地的文件模块,但是对于node_modules当中那些第三方的模块,并不能像webpack直接通过模块的名称导入对应的模块,那为了抹平这样的一个差异,rollup官方给出了一个
rollup-plugin-node-resolve
通过这个插件,我们就可以在代码当中,直接去使用模块名称导入对应的模块
我们来安装这个插件
npm i rollup-plugin-node-resolve
然后我们再装一个lodash
库,lodash是对各种方法、函数的封装库
npm i lodash-es
注意,我们这里安装的是lodash esm版本。而不是lodash普通版本,是因为rollup默认只能处理esm模块。如果说我们要使用普通版本,我们需要做额外的处理
去rollup.config.js
文件引入该插件
//引入rollup-plugin-json插件
const json = require('rollup-plugin-json')
const resolve = require('rollup-plugin-node-resolve')
module.exports = {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
},
//使用json插件
plugins: [
json(),
resolve()
]
}
再次打包我们发现bundle.js 里面就已经有lodash的代码了
rollup的设计就是只处理esm模块打包,如果代码中导入commonjs模块那是不被支持的,但是呢目前,还有大量的npm模块使用commonjs的方式导出成员,所以为了兼容这些模块rollup官方呢,又给出了一个插件rollup-plugin-commonjs
安装插件
npm i rollup-plugin-commonjs
引入插件
创建一个commonjs的模块cjs-module.js
module.exports = {
foo: 'bar'
}
在index.js里直接导入commonjs模块
打包
./node_modules/.bin/rollup --config rollup.config.js
找到bundle.js 拖到最下面,就发现commonjs的成员都被打包进去了。
在rollup最新的版本中,已经支持代码拆分了,同样可以使用符合esm标准的动态导入 Dynamic Import
的方式去实现模块的按需加载,rollup内部也会自动去处理代码拆分,也就是分包
在入口文件index.js中注释掉静态导入的代码
//动态导入的方式
import('./logger').then(({log})=>{
log('code splitting~')
})
import返回的是一个promise对象,它的then方法可以拿到模块导入的一个对象,由于模块导出的成员都会放到这个对象当中,所以我们就可以通过解构的方式去提取出来里面的方法
我们尝试运行打包
./node_modules/.bin/rollup --config
它会返回一个错误,意思是我们使用 code-splitting
代码拆分的方式打包它要求我们的输出格式不能是iife
这种格式,原因也很简单因为自执行函数会把所有的模块放到同一个函数当中,它并没有像webpack一样有一些引导代码
所以说,它没有办法实现代码拆分 code-splitting
,如果你要使用 code-splitting
的话,你就要使用 amd 或者 commonjs这样一些其它的格式,那我们在浏览器的环境当中,我们只能使用amd的标准格式,所以这里我们使用amd的格式输出打包结果
./node_modules/.bin/rollup --config --format amd
又报错了
是告诉我们, code-splitting
的方式,它需要输出多个文件,因为需要输出多个文件,我们就不能使用file
这种配置方式,因为file
这种方式是指定单个文件的输出文件名,那我们要输出多个文件的话,我们需要使用dir
这个参数
由于我们要改的东西比较多,于是可以直接去配置文件中修改。
dir: 'dist',//输出目录
format: 'amd'//格式为amd
打包
./node_modules/.bin/rollup --config
然后输出目录dist就多了一个入口文件的打包文件和一个动态导入的打包文件
它们都是通过amd的方式输出的。
rollup同样支持多入口打包,而且对于多入口那些公共的部分也会自动提取到单个文件当中作为独立的bundle
我们新建一个入口文件album.js
import fetchApi from './fetch';
import {log} from './logger'
fetchApi('/posts?albumId=1').then(data => {
data.forEach(item => {
log(item);
})
})
同时 index.js 这个入口文件的内容如下
import fetchApi from './fetch';
import {log} from './logger'
fetchApi('/posts').then(data => {
data.forEach(item => {
log(item);
})
})
还有一个esm,fetch.js
export default endpoint => {
return fetch(`https://jsonplaceholder.typicode.com${endpoint}`)
.then(response => response.json())
}
修改多入口打包的方式也非常简单,只需要把配置文件rollup.config.js
的input属性改成一个数组就可以了,这里需要注意,因为多入口打包内部会提取公共模块,也就是说内部会使用代码拆分,那输出格式就不能使用iife这种自调用函数的输出格式了。
// input: 'src/index.js',
// input: ['src/index.js', 'src/album.js'],//改成一个数组就可以了
//多入口打包,也可以使用对象的方式重命名入口文件
input:{
foo:'src/index.js',
bar:'src/album.js'
},
然后我们删除 dist目录,重新打包
./node_modules/.bin/rollup --config
我们可以看一下,分别输出的结果
bar.js
define(['./logger-43f15a50'], (function (logger) { 'use strict';
logger.fetchApi('/posts?albumId=1').then(data => {
data.forEach(item => {
logger.log(item);
});
});
}));
foo.js
define(['./logger-43f15a50'], (function (logger) { 'use strict';
// //导入模块成员
logger.fetchApi('/posts').then(data => {
data.forEach(item => {
logger.log(item);
});
});
}));
logger-43f15a50.js,这个就把公共用到的部分,单独打包成一个bundle。
define(['exports'], (function (exports) { 'use strict';
var fetchApi = endpoint => {
return fetch(`https://jsonplaceholder.typicode.com${endpoint}`)
.then(response => response.json())
};
const log = msg =>{
console.log('-------INFO-----------');
console.log(msg);
console.log('----------------------');
};
exports.fetchApi = fetchApi;
exports.log = log;
}));
这里需要注意的是,amd这种输出格式的js文件,不能直接引用到页面上,而是需要通过实现amd标准的库去加载。
我们在dist目录中新建index.html 去尝试加载一下打包的amd格式的js文件
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!--<script src="./dist/foo.js"></script>-->
<script src="https://cdn.jsdelivr.net/npm/requirejs@2.3.6/require.js" data-main="foo.js"></script>
</body>
</html>
然后打开开发者工具,发现foo.js 已经被正常引入进来,也正常工作了
通过探索和尝试,我们发现rollup确实有它的优势
1.输出的结果更加扁平,那执行效率就会更高
2.自动移除未引用的代码,也就是 tree shaking
3.打包结果基本上和手写的代码一致的,打包结果对于开发者而言还是可以正常阅读的
缺点也很明显
1.加载非esm的第三方模块就比较复杂,需要插件。
2.模块最终都被打包到一个函数中,无法实现 HMR(热更新),模块热替换的开发体验。
3.在浏览器环境中,代码拆分依赖 amd的库,因为它的代码拆分就必须使用像amd这样的输出格式
综合以上的特点,如果说我们开发一个应用程序,我们就肯定需要去引入大量的第三方模块,同时我们也需要像HMR一样提升开发体验,而且我们应用一旦大了过后,还设计到必须要去分包,那这些需求呢,在rollup上都会有一些欠缺,如果我们开发的是一个javascript的类库或者框架,那这些优点就特别有必要,那它这些缺点都可以忽略,所以说,很多知名的框架 像 react vue它都使用rollup作为打包器,而并非是webpack,但是,到目前为之,开源社区大多数人还是希望这两个工具,可以共同存在相互发展相互支持和借鉴,那原因也很简单,就是希望更专业的工具去做更专业的事情
总结呢 就是 webpack是大而全,rollup是小而美
那在对两者之间的选择上,如果我们正在开发应用程序,建议大家使用webpack,如果是开发类库或者框架,建议 rollup,不过这不是一个绝对的标准,只是一个经验法则,因为rollup同样也可以构建绝大多数的应用程序,webpack同样可以构建类库或者框架,只不过相对来讲有种术业有专攻的感觉,随着近几年webpack的发展,rollup的优势已经逐渐被抹平了,例如,像 rollup这种扁平化输出,在wepack中就可以使用对应的插件去输出。
到这里视频里面的教程已经完结了。下面是我自己的一些需要用到插件和对rollup的一些探索。
官方提供的 @rollup/plugin-babel
,只有新版 rollup2 才有 @rollup/xxx 作用域开头的子依赖库,能不用原来 rollup-xxx 开头的库就不用,在 babel7 和 vue3 都在作用域管理的大潮下,我们要向作用域包看齐。
npm i -D @rollup/plugin-babel
@rollup/plugin-terser
rollup-plugin-scss
./node_modules/.bin/rollup --config -w