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