# 轻量化Library打包工具
# 简介
Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。
Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而你的项目不必携带其他未使用的代码。ES6 模块最终还是要由浏览器原生实现,但当前 Rollup 可以使你提前体验。
# Why is rollup
Rollup 着眼于未来,采用原生的ES 标准的模块机制进行构建,未来ES 规范肯定会由浏览器实现,也是JavaScript语言明确的发展方向。机制更加优于CommonJS
CommonJS 是在ES规范之前被提出的一种暂时性性解决方案,是一种特殊的传统格式,
相比较ES模块允许进行静态分析,从而实现像 tree-shaking 的优化,并提供诸如循环引用和动态绑定等高级功能
Tree-shaking, 也被称为 "live code inclusion," 它是清除实际上并没有在给定项目中使用的代码的过程,但是它可以更加高效。
# 构建工具对比
| 打包工具 | 体积 | 注入代码 | code spliting | dynamic import | Tree-shaking |
|---|---|---|---|---|---|
| webpack | large | more | ✅ | ✅ | ❌ |
| rollup | small | less | ❌ | ❌ | ✅ |
webpack 是一款强大的 bundle 构建工具,通过 loader机制可以处理各种类型的文件,良好的 code splitting和dynamic import支持使得webpack 成为了应用程序,单页应用的全能型的的打包工具。
但是因为其打包体积相对较大,注入代码更多,没有良好的Tree-shaking所以在Library 的打包工作中,不如rollup打包的精简。
# 配置
rollup 定位是一款轻量级的构建工具,其配置也相对很简单,但是由于不支持CommonJs,在配置rollup-plugin-commonjs 将CommmonJS 转化成ES 模块方式的时候,需要配置babel 转译
由于babel 7+升级改动比较大,命名空间改动,市面上的教程大部分说的不够清楚,而且基本上停留在babel 6的配置,导致一直抛(plugin commonjs) SyntaxError: Unexpected token
在后文中会着力介绍这个部分,分析babel 7+配置以及各个presets 和plugins 的作用。
# 安装rollup
npm install --save-dev rollup
# 配置文件
在项目根目录创建rollup.config.js文件。
rollup 配置比较的简洁,一般包括input,output,plugins,external
这是一段简单的配置文件
export default {
input: 'src/index.js',
output: {
name: 'amapUpper',
file: 'dist/amapUpper.js',
format: 'umd' //兼容模式
},
external: ['@amap/amap-jsapi-loader'],// 配置引入的包是否要打包,在这里配置的会忽略掉,不打包到我们的程序里面
plugins: []
};
2
3
4
5
6
7
8
9
10
当你的配置文件需要配置多个打包策略的时候,你还可以这样配置
export default [{
input: 'main-a.js',
output: {
file: 'dist/bundle-a.js',
format: 'cjs'
}
}, {
input: 'main-b.js',
output: [
{
file: 'dist/bundle-b1.js',
format: 'cjs'
},
{
file: 'dist/bundle-b2.js',
format: 'es'
}
]
}]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
你还可以使用多个配置文件,使用--config来使用配置文件
# pass a custom config file location to Rollup
rollup --config my.config.js
2
WARNING
使用了umd模式必须指定Name for UMD export
# 配置rollup插件
配置gollup 的plugin 类似babel 全部移植到了@rollup下,在官方提供的 一站式配置商店,可以下载所需的插件,
作用在于可维护学习被误导成本降低,更有利于长期维护。
# 插件是什么
rollup plugin 是一个遵循rollup插件规范的object,一般通过一个工厂函数返回一个对象实现
一段简单的示例:
export default function myExample () {
return {
name: 'my-example', // this name will show up in warnings and errors
resolveId ( source ) {
if (source === 'virtual-module') {
return source; // this signals that rollup should not ask other plugins or check the file system to find this id
}
return null; // other ids should be handled as usually
},
load ( id ) {
if (id === 'virtual-module') {
return 'export default "This is virtual!"'; // the source code for "virtual-module"
}
return null; // other ids should be handled as usually
}
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
然后在配置文件中引用该插件,在plugins中,传入执行返回的对象
import myExample from 'myExample'
plugins: [ myExample() ]
2
3
4
# 插件对象遵循的规范
A Rollup plugin is an object with one or more of the properties, build hooks, and output generation hooks
# 插件执行顺序
熟悉webpack loader机制的都应该知道loader 实际上是从右到左,自下而上执行的,在页头的 rollup打包js的注意点 里面提到的错误记录/错误2里面,类比webpack loader机制,
其实是一个错误的类比,实际上rollup的plugin 机制是从左往右,自上而下而下的执行顺序。
一个测试例子
// rollup.config.js
function myExample1() {
return {
name: 'my-example1', // this name will show up in warnings and errors
resolveId(source) {
console.log('111 resolve_______________-------------------------------------------');
return null; // other ids should be handled as usually
},
load(id) {
console.log('111 load_______________-------------------------------------------');
return null; // other ids should be handled as usually
}
};
}
function myExample2() {
return {
name: 'my-example1', // this name will show up in warnings and errors
resolveId(source) {
console.log('222 resolve_______________-------------------------------------------');
return null; // other ids should be handled as usually
},
load(id) {
console.log('222 load_______________-------------------------------------------');
return null; // other ids should be handled as usually
}
};
}
export default {
input: 'src/index.js',
output: {
name: 'test',
file: 'dist/test.js',
format: 'umd'
},
plugins: [
myExample1(),
myExample2()
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

可以看到load,resolve 钩子函数都是从左往右,自上而下而下的执行顺序。
# 配置常用的插件
下面介绍正常打包中需要用到的插件,和一般配置。
@rollup/plugin-json原rollup-plugin-json@rollup/plugin-commonjs原rollup-plugin-commonjs@rollup/plugin-node-resolve原rollup-plugin-node-resolverollup-plugin-terser
首先,需要作为开发依赖安装这些插件
npm i @rollup/plugin-json -D
npm i @rollup/plugin-commonjs -D
npm i @rollup/plugin-node-resolve -D
npm i rollup-plugin-terser -D
2
3
4
然后需要引用配置文件
import json from '@rollup/plugin-json';
import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
export default {
...
plugins: [
json(),
terser(),
nodeResolve(),
commonjs(),
babel({
exclude: '*', // 排除node_modules 下的文件
runtimeHelpers: true // 防止生成多个 打包helper 函数
}),
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 问题总结
# commonjs 插件和 babel7+ 配置
在跟着 rollup 搭建打包 JS 一文配置的过程中,用到的插件不是官方一站式插件提供的长期维护版本,遇到了 rollup-plugin-commonjs 插件打包报错的问题
![[!] (plugin commonjs) SyntaxError: Unexpected token (25:8)](/assets/img/rollup-log2.b0c1d596.png)
针对使用了放弃维护的rollup-plugin-commonjs插件的打包抛出的这个SyntaxError,可以有两种解决方案。推荐方案二。
# 解决方案一 配置babel
查找了很多资料都显示,应该是babel 配置问题,主要分为:
babel配置问题babel转译执行顺序问题
综合这些资料,这个报错应该是由于该commonjs 插件库 被移动到 @rollup/plugin-commonjs下维护,导致非长期维护版本缺少某些转译后的辅助函数,属性,导致抛出异常。
所以需要babel 辅助编译之后才能正确执行commonjs 插件。
babel 配置的一些问题
由于babel 7+版本中,将babel 的各个库全部移植到了@babel/***下统一官方维护,而市面上大部分的babel教程实际上没有对这点进行详细说明,而且教程里安装babel组件的时候并没有指定版本,实际上导致可能安装了7+ 版本的babel核心库,而安装了放弃维护的babel-plugins-***下的插件预设之类的。就会导致一系列的问题。
这里要注意一下这个@这个符号,这个是只有babel7才特有的,babel6都木有,市面上大量代码都是基于6的所以要特别注意,安装的时候要使用
npm install i -S @babel\cli 而不能是npm install i -S babel-cli了
这是 babel 7 的一大调整,原来的 babel-xx 包统一迁移到babel域 下 - 域由 @ 符号来标识
配置
由于这里使用@rollup/plugin-babel,执行babel 转译 所以不需要安装@babel/cli命令行工具。
首先我们需要安装:
- babel核心包
@babel/core - babel预设包
@babel/preset-env - 统一的模块化的helper
@babel/runtime - helper 的自动化引入工具
@babel/plugin-transform-runtime
npm i -D @babel/core
npm i -D @babel/preset-env
npm i -D @babel/runtime
npm i -D @babel/plugin-transform-runtime
2
3
4
然后在项目根目录下创建.babelrc的配置文件
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"browsers": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
}
]
],
"plugins": ["@babel/plugin-transform-runtime"]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
然后如果你用的是rollup-plugin-commonjs非常期维护版本的插件的话,由于和babel有依赖关系,你需要:
在commonjs插件之前引入babel插件,并配置需要匹配编译的文件,目录。和编译配置
// rollup.config.js
export default {
input: 'src/index.js',
output: {
name: 'amapUpper',
file: 'dist/amapUpper.js',
format: 'umd'
},
plugins: [
babel({
exclude: '/node_modules/**', // 排除node_modules 下的文件
runtimeHelpers: true // 与plugin-transform-runtime 插件对应配置,生成统一化helper。
}),
commonjs()
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 解决方案二
使用rollup新的官方提供的移动后的一站式插件库里面的插件
使用了新的插件库之后,babel还是需要配置的,只是解决了commonjs 插件依赖babel的问题。
而babel 实际上是解决各种浏览器,引擎之间的差异,而存在的。所以为了更好的支持,需要配备良好的babel配置。