前端進階(6) - webpack 以外的另外一種選擇:rollup

webpack 以外的另外一種選擇:rollup

webpack 對前端來講是再熟悉不過的工具了,它提供了強大的功能來構建前端的資源,包括 html/js/ts/css/less/scss ... 等語言腳本,也包括 images/fonts ... 等二進制文件。css

其實,webpack 發起之初主要是爲了解決如下兩個問題:html

  1. 代碼拆分(Code Splitting): 能夠將應用程序分解成可管理的代碼塊,能夠按需加載,這樣用戶即可快速與應用交互,而沒必要等到整個應用程序下載和解析完成才能使用,以此構建複雜的單頁應用程序(SPA);
  2. 靜態資源(Static Assets): 能夠將全部的靜態資源,如 js、css、圖片、字體等,導入到應用程序中,而後由 webpack 使用 hash 重命名須要的資源文件,而無需爲文件 URL 增添 hash 而使用 hack 腳本,而且一個資源還能依賴其餘資源。

正是由於 webpack 擁有如此強大的功能,因此 webpack 在進行資源打包的時候,就會產生不少冗餘的代碼(若是你有查看過 webpack 的 bundle 文件,便會發現)。前端

好比,把 export default str => str; 這段代碼用 webpack 打包就會獲得下面的結果:vue

/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // Flag the module as loaded
/******/         module.l = true;
/******/
/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

/* harmony default export */ __webpack_exports__["default"] = (str => str);


/***/ })
/******/ ]);

這在如下的一些情境中就不過高效,須要尋求更好的解決方案:react

  1. 須要 js 高效運行。由於 webpack 對子模塊定義和運行時的依賴處理(__webpack_require__),不只致使文件體積增大,還會大幅拉低性能;
  2. 項目(特別是類庫)只有 js,而沒有其餘的靜態資源文件,使用 webpack 就有點大才小用了,由於 webpack bundle 文件的體積略大,運行略慢,可讀性略低。

在這種狀況下,就想要尋求一種更好的解決方案,這即是 rollup.webpack

如今已經有不少類庫都在使用 rollup 進行打包了,好比:react, vue, preact, three.js, moment, d3 等。git

1. 工具

安裝es6

npm i -g rollup          # 全局安裝

npm i -D rollup          # 本地安裝

使用github

rollup -c                # 使用一個配置文件,進行打包操做

更多詳細的用法,參考 rollup.js - command-line-flags.web

2. 配置

rollup 的配置與 webpack 的配置相似,定義在 rollup.config.js 文件中,好比:

// rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    file: 'bundle.js',
    // amd, cjs, esm, iife, umd, system
    format: 'cjs'
  }
};

經常使用的幾個配置項:

  1. input: 源碼入口文件,通常是一個文件,如 src/index.js
  2. output: 定義輸出,如文件名,目標目錄,輸出模塊範式(es6, commonjs, amd, umd, iife 等),模塊導出名稱,外部庫聲明,全局變量等。
  3. plugins: 插件,好比 rollup-plugin-json 可讓 rollup 從 .json 文件中導入 json 數據。

更多詳細的配置,參考 rollup.js - configuration-files.

3. rollup 與 webpack 對比

先拿段代碼來來看看他們打包以後各自是什麼效果。

源代碼

# 目錄
|-- src/
    |-- index.js
    |-- prefix.js
    |-- suffix.js

    
# prefix.js
const prefix = 'prefix';

export default str => `${prefix} | ${str}`;
    
    
# suffix.js
const suffix = 'suffix';

export default str => `${str} | ${suffix}`;


# index.js
import prefix from './prefix';
import suffix from './suffix';

export default str => suffix(prefix(str));

配置

# webpack.config.js
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'dist/webpack.bundle.js',
    library: 'demo',
    libraryTarget: 'umd'
  }
};


# rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/rollup.bundle.js',
    name: 'demo',
    format: 'umd'
  }
};

運行

# webpack 打包
webpack


# rollup 打包
rollup -c

webpack.bundle.js

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();
    else if(typeof define === 'function' && define.amd)
        define([], factory);
    else if(typeof exports === 'object')
        exports["demo"] = factory();
    else
        root["demo"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // Flag the module as loaded
/******/         module.l = true;
/******/
/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__prefix__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__suffix__ = __webpack_require__(2);



/* harmony default export */ __webpack_exports__["default"] = (str => Object(__WEBPACK_IMPORTED_MODULE_1__suffix__["a" /* default */])(Object(__WEBPACK_IMPORTED_MODULE_0__prefix__["a" /* default */])(str)));


/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
const prefix = 'prefix';

/* harmony default export */ __webpack_exports__["a"] = (str => `${prefix} | ${str}`);


/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
const suffix = 'suffix';

/* harmony default export */ __webpack_exports__["a"] = (str => `${str} | ${suffix}`);


/***/ })
/******/ ]);
});

rollup.bundle.js

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.demo = factory());
}(this, (function () { 'use strict';

    const prefix = 'prefix';

    var prefix$1 = str => `${prefix} | ${str}`;

    const suffix = 'suffix';

    var suffix$1 = str => `${str} | ${suffix}`;

    var index = str => suffix$1(prefix$1(str));

    return index;

})));

其實,你也基本上看出來了,在這種場景下,rollup 的優點在哪裏:

  1. 文件很小,幾乎沒什麼多餘代碼,除了必要的 cjs, umd 頭外,bundle 代碼基本和源碼差很少,也沒有奇怪的 __webpack_require__, Object.defineProperty 之類的東西;
  2. 執行很快,由於沒有 webpack bundle 中的 __webpack_require__, Object.defineProperty 之類的冗餘代碼;
  3. 另外,rollup 也對 es 模塊輸出及 iife 格式打包有很好的支持。

4. 結論

rollup 相對 webpack 而言,要小巧、乾淨利落一些,但不具有 webpack 的一些強大的功能,如熱更新,代碼分割,公共依賴提取等。

因此,一個不錯的選擇是,應用使用 webpack,類庫使用 rollup。

5. 後續

更多博客,查看 https://github.com/senntyou/blogs

做者:深予之 (@senntyou)

版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

相關文章
相關標籤/搜索