rollup从设计之初就是面向ES module
的,它诞生时AMD、CMD、UMD的格式之争还很火热,做者但愿充分利用ES module
机制,构建出结构扁平
,性能出众
的类库。前端
ES module的设计思想是尽可能的静态化
,使得编译时就能肯定模块的依赖关系
,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时
肯定这些东西,举例来讲:node
这些设计虽然使得灵活性不如CommonJS的require
,但却保证了 ES modules 的依赖关系是肯定的,和运行时的状态无关,从而也就保证了ES modules是能够进行可靠的静态分析的。webpack
咱们经过一个案例看一下webpack
和rollup
打包后的代码结构。web
源文件:json
// index.js
import a from './a.js'
import b from './b.js'
export default () => {
console.log(a())
}
// a.js
export default () => 'a'
// b.js
export default () => 'b'
复制代码
webpack打包生成:浏览器
(function(modules) { // webpackBootstrap
// 大量的runtime代码
// ...
})
({
"./src/a.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (() => 'a');\n\n//# sourceURL=webpack:///./src/a.js?");
}),
"./src/b.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (() => 'b');\n\n//# sourceURL=webpack:///./src/b.js?");
}),
"./src/index.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _a_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a.js */ \"./src/a.js\");\n/* harmony import */ var _b_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b.js */ \"./src/b.js\");\n\n \n\n/* harmony default export */ __webpack_exports__[\"default\"] = (() => {\n console.log(Object(_a_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])())\n});\n\n//# sourceURL=webpack:///./src/index.js?");
})
})
复制代码
rollup打包生成:缓存
var test = (function () {
'use strict';
var a = () => 'a';
var index = () => {
console.log(a());
};
return index;
}());
复制代码
首先,webpack致力于复杂SPA的模块化构建,优点在于:性能优化
rollup致力于打造性能出众的类库,有以下优点:bash
可读性好
干净
,没有什么多余的代码,只是将各个模块按照依赖顺序拼接起来,全部模块构建在一个函数内(Scope Hoisting), 执行效率更高。相比webpack(webpack打包后会生成__webpack_require__等runtime代码),rollup拥有无可比拟的性能优点,这是由依赖处理方式决定的,编译时依赖处理(rollup)天然比运行时依赖处理(webpack)性能更好
es
模块文件(webpack不支持导出es模块)固然,rollup也有明显的缺点:前端框架
commonjs以及umd依赖
经过以上的对比能够得出,构建App应用
时,webpack比较合适,若是是类库(纯js项目)
,rollup更加适合。
webpack构建App的优点体如今如下几方面:
插件生态
,主流前端框架都有对应的loaderHMR
,按需加载
,公共模块
提取等都是开发App应用必要的特性图片自动base64,资源缓存(chunkId),按路由作代码拆分,懒加载
等__webpack_require__
实现各类类型的模块依赖问题)rollup的优点在于构建高性能的bundle
,这正是类库所须要的。
tree-shaking能够理解为经过工具"摇"咱们的JS文件,将其中用不到的代码"摇"掉,属于性能优化的范畴。
tree-shaking较早由rollup
实现,后来webpack2
也借助于UglifyJS
实现了tree-shaking的功能。
tree-shaking的本质是借助ES module的静态分析
来消除无用的
js代码,无用代码有如下几个特征:
rollup打包时的tree-shaking案例:
// add.js:
export default (a, b) => {
return a + b
}
// index.js:
import add from './add.js'
export default () => {
add(1, 2)
}
// 打包后bundle.js:
var index = () => {
};
export default index;
复制代码
add(1, 2)
的执行结果在index.js
中没有被用到,所以在bundle.js
中被'摇'掉了。
若是函数中存在反作用
,那么tree-shaking会失效:
// add.js:
export default (a, b) => {
window.a = 'a'
return a + b
}
// ...
// bundle.js:
var add = (a, b) => {
window.a = 'a';
return a + b
};
var index = () => {
add(1, 2);
};
export default index;
复制代码
因此咱们尽可能不要写带有反作用的代码。
首先在项目中安装rollup:
yarn add rollup -D
复制代码
package.json中加入构建脚本命令:
{
"scripts": {
"build": "rollup -c"
}
}
复制代码
rollup 支持命令行
和JS API
两种调用方式,咱们重点来看下命令行配置文件中的几个核心属性:
// rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel';
export default [{
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'umd',
name: 'test'
},
plugins: [
resolve(),
commonjs(),
babel({
exclude: 'node_modules/**'
})
]
}]
复制代码
iife
: 自执行函数, 可经过 <script>
标签加载amd
: 浏览器端的模块规范, 可经过 RequireJS 可加载cjs
: Node 默认的模块规范, 可经过 Webpack 加载umd
: 兼容 IIFE, AMD, CJS 三种模块规范es
: ES module 规范, 可用 Webpack, Rollup 加载rollup为了方便类库的使用者进行tree-shaking
,提供了es
的构建格式:
源文件:
// add.js:
export default (a, b) => a + b
// index.js:
import add from './add.js'
export default () => {
var result = add(1, 2)
console.log(result)
}
复制代码
rollup按照es
的格式构建:
var add = (a, b) => {
return a + b
};
var index = () => {
var result = add(1, 2);
console.log(result);
};
export default index;
复制代码
能够看出,咱们获得了一个基于ES module规范
的bundle,此时读者可能会有这样的疑问:应用项目中一般会设置babel屏蔽node_module
目录下的文件,若是将pkg.main
指向当前ES module规范的bundle
,项目最终打包后的bundle中会包含ES module
代码。
为了解决上述问题,rollup最先提出了pkg.module
,配置导出格式为es
的文件的路径,打包工具在遇到pkg.module
字段时,会优先使用。
综上所述,类库经过rollup能够设置打包出两份文件
,一份umd(按照实际须要可选其余)
,一份es
,将它们的路径分别设置为package.json中的main
和module
的值。这样就能方便类库的使用者进行tree-shaking
。
rollup经过external
+ output.globals
来标记外部依赖,以lodash为例:
// rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
export default [
{
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife',
name: 'test',
globals: {
'lodash': '_'
}
},
external: [
'lodash'
],
plugins: [
resolve(),
commonjs()
]
}
]
// index.js
import lodash from 'lodash'
export default () => {
console.log(lodash)
}
// bundle.js
var test = (function (lodash) {
'use strict';
lodash = lodash && lodash.hasOwnProperty('default') ? lodash['default'] : lodash;
var index = () => {
console.log(lodash);
};
return index;
}(_));
复制代码
rollup同时提供了一些插件来解决压缩
,babel转换
等问题,这里列举几个经常使用的插件:
rollup-plugin-alias
: 配置module的别名rollup-plugin-babel
: 打包过程当中使用Babel进行转换, 须要安装和配置Babelrollup-plugin-eslint
: 提供ESLint能力, 须要安装和配置ESLintrollup-plugin-node-resolve
: 解析node_modules 中的模块rollup-plugin-commonjs
: 转换 CJS -> ESM, 一般配合上面一个插件使用rollup-plugin-replace
: 相似于webpack的DefinePlugin其余配置见:rollup官网
最后感谢您花时间阅读这篇文章,但愿这篇文章能对您有所帮助。
参考文章: 1.www.zhihu.com/question/41… 2.juejin.im/post/5a4dc8… 3.www.ayqy.net/blog/rollup…
水滴前端团队招募伙伴,欢迎投递简历到邮箱:fed@shuidihuzhu.com