以前翻譯過一篇文章,介紹了經過 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 的熱度會大大消減。
共勉~