资讯专栏INFORMATION COLUMN

前端进阶(6) - webpack 之外的另一种选择:rollup

stefan / 553人阅读

摘要:之外的另一种选择对前端来说是再熟悉不过的工具了,它提供了强大的功能来构建前端的资源,包括等语言脚本,也包括等二进制文件。所以,一个不错的选择是,应用使用,类库使用。

webpack 之外的另一种选择:rollup

webpack 对前端来说是再熟悉不过的工具了,它提供了强大的功能来构建前端的资源,包括 html/js/ts/css/less/scss ... 等语言脚本,也包括 images/fonts ... 等二进制文件。

其实,webpack 发起之初主要是为了解决以下两个问题:

代码拆分(Code Splitting): 可以将应用程序分解成可管理的代码块,可以按需加载,这样用户便可快速与应用交互,而不必等到整个应用程序下载和解析完成才能使用,以此构建复杂的单页应用程序(SPA);

静态资源(Static Assets): 可以将所有的静态资源,如 js、css、图片、字体等,导入到应用程序中,然后由 webpack 使用 hash 重命名需要的资源文件,而无需为文件 URL 增添 hash 而使用 hack 脚本,并且一个资源还能依赖其他资源。

正是因为 webpack 拥有如此强大的功能,所以 webpack 在进行资源打包的时候,就会产生很多冗余的代码(如果你有查看过 webpack 的 bundle 文件,便会发现)。

比如,把 export default str => str; 这段代码用 webpack 打包就会得到下面的结果:

/******/ (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);


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

这在以下的一些情境中就不太高效,需要寻求更好的解决方案:

需要 js 高效运行。因为 webpack 对子模块定义和运行时的依赖处理(__webpack_require__),不仅导致文件体积增大,还会大幅拉低性能;

项目(特别是类库)只有 js,而没有其他的静态资源文件,使用 webpack 就有点大才小用了,因为 webpack bundle 文件的体积略大,运行略慢,可读性略低。

在这种情况下,就想要寻求一种更好的解决方案,这便是 rollup.

现在已经有很多类库都在使用 rollup 进行打包了,比如:react, vue, preact, three.js, moment, d3 等。

1. 工具

安装

npm i -g rollup          # 全局安装

npm i -D rollup          # 本地安装

使用

rollup -c                # 使用一个配置文件,进行打包操作

更多详细的用法,参考 rollup.js - command-line-flags.

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"
  }
};

常用的几个配置项:

input: 源码入口文件,一般是一个文件,如 src/index.js

output: 定义输出,如文件名,目标目录,输出模块范式(es6, commonjs, amd, umd, iife 等),模块导出名称,外部库声明,全局变量等。

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 的优势在哪里:

文件很小,几乎没什么多余代码,除了必要的 cjs, umd 头外,bundle 代码基本和源码差不多,也没有奇怪的 __webpack_require__, Object.defineProperty 之类的东西;

执行很快,因为没有 webpack bundle 中的 __webpack_require__, Object.defineProperty 之类的冗余代码;

另外,rollup 也对 es 模块输出及 iife 格式打包有很好的支持。

4. 结论

rollup 相对 webpack 而言,要小巧、干净利落一些,但不具备 webpack 的一些强大的功能,如热更新,代码分割,公共依赖提取等。

所以,一个不错的选择是,应用使用 webpack,类库使用 rollup。

5. 后续

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

作者:深予之 (@senntyou)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96999.html

相关文章

  • 前端进阶(10) - webpack 之外的另一种选择:parcel

    摘要:便提供了一个额外的选择,对于不喜欢配置的开发者尤其友好,因为没有配置文件,仅有的少量配置项也是从命令行输入。另外会自动识别安装在中的插件,然后导入,而无需手动配置。与相比,零配置是最大的特点与优势,但没有功能强大,也缺少了些灵活性。 webpack 之外的另一种选择:parcel 之前有写过一篇 webpack 之外的另一种选择:rollup,这次算是姊妹篇,介绍另外一个工具 parc...

    sutaking 评论0 收藏0
  • 前端进阶(14) - 如何提升前端性能和响应速度

    摘要:一般建议文件最大不超过。按需加载可以减小首屏加载文件的体积,达到提高响应速度的目的。如果你的项目不需要处理静态资源如图片,也不需要按需加载,并追求前端高性能的话,可以尝试。 如何提升前端性能和响应速度 下面大多是从前端工程化的角度给出的优化建议,如果需要了解语法上的优化,可以参考: 如何提高页面加载速度 编写高效的JavaScript Web前端性能优化进阶 - 完结篇 1. 原生...

    lylwyy2016 评论0 收藏0
  • 前端进阶(14) - 如何提升前端性能和响应速度

    摘要:一般建议文件最大不超过。按需加载可以减小首屏加载文件的体积,达到提高响应速度的目的。如果你的项目不需要处理静态资源如图片,也不需要按需加载,并追求前端高性能的话,可以尝试。 如何提升前端性能和响应速度 下面大多是从前端工程化的角度给出的优化建议,如果需要了解语法上的优化,可以参考: 如何提高页面加载速度 编写高效的JavaScript Web前端性能优化进阶 - 完结篇 1. 原生...

    Airy 评论0 收藏0
  • 前端进阶(9) - js 性能优化利器:prepack

    摘要:性能优化利器性能优化性能优化不外乎从三个角度入手开发者在编写程序时,尽量避免不必要的冗余代码,包括冗余的第三方库首先要避免不必要的冗余代码,包括不必要的闭包不必要的变量与函数声明不必要的模块分割等。 js 性能优化利器:prepack 1. js 性能优化 js 性能优化不外乎从三个角度入手: 1.1 开发者在编写程序时,尽量避免不必要的冗余代码,包括冗余的第三方库 首先要避免不必要的...

    JouyPub 评论0 收藏0
  • 从 1 到完美,写一个 js 库、node 库、前端组件库

    摘要:从到完美,写一个库库前端组件库之前讲了很多关于项目工程化前端架构前端构建等方面的技术,这次说说怎么写一个完美的第三方库。使用导出模块,就可以在使用这个库的项目中构建时使用功能。 从 1 到完美,写一个 js 库、node 库、前端组件库 之前讲了很多关于项目工程化、前端架构、前端构建等方面的技术,这次说说怎么写一个完美的第三方库。 1. 选择合适的规范来写代码 js 模块化的发展大致有...

    rollback 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<