以前翻译过一篇文章,介绍了经过 ES2015 的解构赋值语法引入模块,可让打包工具(browserify)最终编译出来的代码量最小化。vue
却不知在 webpack 1.X 版本是没法利用该特性来避免引入冗余模块代码的,致使打出来的 bundle 文件大小不免略有臃肿。node
今天则向你们介绍一个当红炸子鸡——Rollup.js,经过它可让你的 bundle 最小化,有效减小文件请求大小——以致于连 vue 都迅速地转投它来打包模块。webpack
Tree-shakinggit
在 Rollup 编译模块的过程当中,经过 Tree-shacking 的方式来剔除各模块中最终未被引用到的方法,经过仅保留被调用到的代码块来缩减 bundle 的大小。es6
咱们来看下官网的例子。github
页面入口文件 main.js:web
import { cube } from './maths.js'; console.log( cube( 5 ) ); // 125,即5的立方值
被引如的 math.js 模块以下:npm
// 注意这个方法在入口文件里没有被调用过 //最终会被 Rollup 剔除 export function square ( x ) { return x * x; } //入口文件须要调用到的求立方值的方法 export function cube ( x ) { return x * x * x; }
经过 Rollup 打包以后以下:gulp
'use strict'; function cube ( x ) { // rewrite this as `square( x ) * x` // and see what happens! return x * x * x; } console.log( cube( 5 ) ); // 125
能够很明显地体会到 Tree-shaking 的做用 —— Math 模块里有个从未用到的 square 方法,我们在 bundle 文件里把它消灭掉了。babel
另外 TS 会抽取引用到的模块内容,将它们置于同一个做用域下,进而直接用变量名就能够访问各个模块的接口;而不像 webpack 这样每一个模块外还要包一层函数定义,再经过合并进去的 define/require 相互调用。
固然这种方法须要 ES2015 的解构赋值语法来配合,多亏了它,Rollup 才能有效地对模块内容进行可靠的静态分析。
使用方式
安装天然不用说,走 npm 的老套路:
npm i rollup
执行打包的方式也是简单到爆:
rollup src/main.js -o rel/bundle.js
这意味着将入口文件 src/main.js 打包为 rel/bundle.js 文件。
不少时候咱们开发走的 ES2015 模块语法,但最终编译出来的模块但愿它能走 commonjs 语法,只须要加上 -f cjs 运行时参数(f for format)便可:
rollup src/main.js -o rel/bundle.js -f cjs
固然,若是你想编译为其它格式,能够把 cjs 更换为:
amd / es6 / iife / umd
咱们分别来个参考~ 假设入口文件 src/main.js 以下:
var name = 'VaJoy'; function main () { console.log(name); } export default main;
编译为各类模式后的bundle:
//////////////////////////////commonjs(-f cjs) 'use strict'; var name = 'VaJoy'; function main () { console.log(name); } module.exports = main; //////////////////////////////AMD(-f amd) define(function () { 'use strict'; var name = 'VaJoy'; function main () { console.log(name); } return main; }); //ES2015/ES6(-f es6) var name = 'VaJoy'; function main () { console.log(name); } export default main; //////////////////////////////Global(-f iife) //注意该方法须要经过配置文件形式来执行(见下一节) var main = (function () { 'use strict'; var name = 'VaJoy'; function main () { console.log(name); } return main; }()); //////////////////////////////UMD(-f umd) //注意该方法须要经过配置文件形式来执行(见下一节) (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.main = factory()); }(this, function () { 'use strict'; var name = 'VaJoy'; function main () { console.log(name); } return main; }));
配置文件
和 webpack 同样,rollup 也支持经过配置文件来实现更灵活的功能。
咱们在项目根目录新建一个 rollup.config.js :
export default { entry: 'src/main.js', format: 'cjs', dest: 'rel/bundle.js' // 输出文件 };
而后执行
rollup -c
便可经过默认配置文件(rollup.config.js)所设置的信息来进行打包。
若是你的配置文件另有其名(例如“rollup.config.dev.js”),在后面加上配置文件名便可:
rollup -c rollup.config.dev.js
Rollup 也支持使用插件,写到配置对象的 plugin 里便可,这里咱们以 rollup-plugin-babel 为例:
import babel from 'rollup-plugin-babel'; export default { entry: 'src/main.js', format: 'cjs', plugins: [ babel() ], dest: 'rel/bundle.js' };
比较不爽的是,babel 的预设不像 webpack 能够直接写在配置文件里,而仍是得独立写个“src/.babelrc”(注意咱们能够写在 src 下,而不是非得放在项目根目录下):
{ "presets": ["es2015-rollup"] }
注意咱得确保安装了 rollup-plugin-babel 和 babel 预设 babel-preset-es2015-rollup:
npm i rollup-plugin-babel babel-preset-es2015-rollup
这时候就能配合 babel 一块儿把 ES6 的模块编译为 ES5 的 bundle 了。
更多有趣的插件能够在 rollup 项目组织里找,貌似没有 webpack 那样专门有个插件列表页汇总,这点找起来不太方便。
Rollup 也支持直接在模块中来被调用执行,这样很方便跟 grunt/gulp 等工具进行协做。
如咱们修改 rollup.config.dev.js 内容为:
var rollup = require( 'rollup' ); var babel = require('rollup-plugin-babel'); rollup.rollup({ entry: 'src/main.js', plugins: [ babel() ] }).then( function ( bundle ) { bundle.write({ format: 'umd', moduleName: 'main', //umd或iife模式下,若入口文件含 export,必须加上该属性 dest: 'rel/bundle.js' }); });
而后用 node 直接执行
node rollup.config.dev.js
能够获得同样的执行结果。
注意 “rollup.rollup()”返回一个带着 bundle 做为 resolve 回调参数的 Promise 对象,咱们常规直接使用语法糖 bundle.write 来打包输出文件:
bundle.write({ format: 'umd', moduleName: 'main', dest: 'rel/bundle.js' });
其等价于
var result = bundle.generate({ //生成一个 bundle + sourcemap format: 'umd', moduleName: 'main', dest: 'rel/bundle.js', }); fs.writeFileSync( 'rel/bundle.js', result.code );
SourceMap
为了方便调试编译后的文件,rollup 确定不会忘记添加 source map 功能,并且其配置也很是简单:
{ format: 'umd', moduleName: 'main', dest: 'rel/bundle.js', sourceMap: true //加上这里便可 }
这样编译后,rollup 会自动生成一个 rel/bundle.js.map 关联到 rel/bundle.js 中。
也能够将其直接内联在 bundle 里而不是独立生成一个 map 文件:
{ format: 'umd', moduleName: 'main', dest: 'rel/bundle.js', sourceMap: 'inline' }
若但愿 map 文件能够自定义位置和名称,就得使用上面稍微提到的 bundle.generate 方法了:
var result = bundle.generate({ //生成一个 bundle + sourcemap //... }); fs.writeFileSync( 'rel/bundle.js', result.code ); fs.writeFileSync( 'map/bundle.js.map', result.map.toString() );
issue
Rollup 虽然利用 ES6 的特性帮咱节省了很多文件大小,但它并无相似 webpack 的 -p 参数帮你压缩混淆文件。
所以即便是官方文档也推荐配合使用 UglifyJS 来进一步缩小 bundle 体积。
另外 webpack2 已经出来好几款 beta 版本了,一样也加上了对 Tree-shaking 的支持,相信 webpack2 出来后,Rollup 的热度会大大消减。
共勉~