Rollup

Posted by huangqing on November 18, 2020

Rollup

rollup是一款小巧的javascript模块打包工具,更适合于库应用的构建工具;可以将小块代码编译成大块复杂的代码,基于ES6 modules,自动进行 Tree-shaking,它可以让你的 bundle 最小化,有效减少文件请求大小。

全局安装

npm install --global rollup

命令行

//该命令编译 main.js 生成 bundle.js, --format cjs 意味着打包为 node.js 环境代码
rollup --input main.js --output bundle.js --format cjs
-i, --input <filename>      要打包的文件(必须)
-o, --file <output>         输出的文件 (如果没有这个参数,则直接输出到控制台)
-f, --format <format>       输出的文件类型 (amd, cjs, esm, iife, umd)
-e, --external <ids>        将模块ID的逗号分隔列表排除
-g, --globals <pairs>       以`module ID:Global` 键值对的形式,用逗号分隔开 
                              任何定义在这里模块ID定义添加到外部依赖
-n, --name <name>           生成UMD模块的名字
-h, --help                  输出 help 信息
-m, --sourcemap             生成 sourcemap (`-m inline` for inline map)
--amd.id                    AMD模块的ID,默认是个匿名函数
--amd.define                使用Function来代替`define`
--no-strict                 在生成的包中省略`"use strict";`
--no-conflict               对于UMD模块来说,给全局变量生成一个无冲突的方法
--intro                     在打包好的文件的块的内部(wrapper内部)的最顶部插入一段内容
--outro                     在打包好的文件的块的内部(wrapper内部)的最底部插入一段内容
--banner                    在打包好的文件的块的外部(wrapper外部)的最顶部插入一段内容
--footer                    在打包好的文件的块的外部(wrapper外部)的最底部插入一段内容
--interop                   包含公共的模块(这个选项是默认添加的)

format选项

rollup提供了五种选项:

  • amd – 异步模块定义,用于像RequireJS这样的模块加载器
  • cjs – CommonJS,适用于 Node 和 Browserify/Webpack
  • es – 将软件包保存为ES模块文件
  • iife – 一个自动执行的功能,适合作为<script>标签。(如果要为应用程序创建一个捆绑包,您可能想要使用它,因为它会使文件大小变小。)
  • umd – 通用模块定义,以amdcjsiife 为一体

使用配置文件

rollup.config.js

const rollup = require("rollup");
// rollup plugin to convert JSON files to ES6 modules
const json = require("rollup-plugin-json");
//Bundle third-party dependencies in node_modules
const resolve = require("rollup-plugin-node-resolve");
//Convert CommonJS modules to ES2015
const commonjs = require("rollup-plugin-commonjs");
const babel = require("rollup-plugin-babel");
const eslint = require("rollup-plugin-eslint").eslint;
const filesize = require("rollup-plugin-filesize");

export default {
    input: 'src/main.js',
    //打包时不进行导入的类库,这些类库采用在外部引用的方式
    //名称为 package.json -> devDependencies中依赖类库的key值
    external: ["jquery"],
    output: {
        file: 'bundle.js',
        format: 'iife',
        name:'',//当format为iife和umd时必须提供,生成包名称,代表你的iife/umd包
        banner:'',
        footer:'',
        intro:'',
        outro:'',
        sourcemap:true,
        //告诉rollup 全局变量jQuery即是jquery
        globals: {
            "jquery": "jQuery"
        }
    },
    plugins:[
        json(),
        resolve(),
        eslint(),
        commonjs(),
        filesize(),
        babel({
            exclude: "node_modules/**",
            runtimeHelpers: true,
            plugins: []
        }),
    ]
};

执行 rollup -c

可以指定与默认 rollup.config.js 文件不同的配置文件:

rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js

使用rollup插件

  • rollup-plugin-alias: 提供modules名称的 alias 和reslove 功能
  • rollup-plugin-babel: 提供babel能力
  • rollup-plugin-buble:编译ES6+语法为ES2015,无需配置,比babel更轻量;
  • rollup-plugin-eslint: 提供eslint能力
  • rollup-plugin-node-resolve: 解析 node_modules 中的模块
  • rollup-plugin-commonjs: 转换 CJS -> ESM, 通常配合上面一个插件使用,加载Node.js里面的CommonJS模块
  • rollup-plugin-serve: 类比 webpack-dev-server, 提供静态服务器能力
  • rollup-plugin-filesize: 显示 bundle 文件大小
  • rollup-plugin-uglify: 压缩 bundle 文件
  • rollup-plugin-terser:代码压缩,取代uglify,支持ES模块。
  • rollup-plugin-replace::替换代码中的变量为指定值。 类比 Webpack 的 DefinePlugin , 可在源码中通过 process.env.NODE_ENV 用于构建区分 Development 与 Production 环境.

使用 .browserslistrc

browserslist叫做目标环境配置表,除了写在package.json里,也可以单独写在工程目录下.browserslistrc文件里。我们用browserslist来指定代码最终要运行在哪些浏览器或node.js环境。Autoprefixer、postcss等就可以根据我们的browserslist,来自动判断是否要增加CSS前缀(例如’-webkit-‘)。我们的Babel也可以使用browserslist,如果你使用了@babel/preset-env这个预设,此时Babel就会读取browserslist的配置。

如果我们的@babel/preset-env不设置任何参数,Babel就会完全根据browserslist的配置来做语法转换。如果没有browserslist,那么Babel就会把所有ES6的语法转换成ES5版本。

chrome 58
ie 11

使用babel

Babel默认只转换JS语法,而不转换新的API,比如IteratorGeneratorSetMapsProxyReflectSymbolPromise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。

举例来说,ES2015在Array对象上新增了Array.from方法。Babel 就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill。(内部集成了core-jsregenerator)

.babelrc

使用 transform-runtime

@babel/runtime 在 babel 7.4 之后大放异彩, 利用 corejs 3 也实现了各种内置对象的支持, 并且依靠 @babel/plugin-transform-runtime 的能力,沙箱垫片和代码复用, 避免帮助函数重复 inject 过多的问题, 该方式的优点是不会污染全局, 适合在类库开发中使用

要使用@babel/plugin-transform-runtime插件,其实只有一个npm包是必须要装的,那就是它自己@babel/plugin-transform-runtime。 对于@babel/runtime及其进化版@babel/runtime-corejs2@babel/runtime-corejs3,我们只需要根据自己的需要安装一个。

{
    "presets": [
        [
            "@babel/env",
            {
                "targets": {
                    //"browsers": "last 2 versions",
                    "chrome": "58",
                    "ie": "10"
                },
                //设置"modules": false,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS,导致 Rollup 的一些处理失败
                "modules": false
            }
        ]
    ],
    "plugins": [
        //将regeneratorRuntime打包进代码中,如 async/await 等es6-8特性
        //需要安装 @babel/runtime @babel/runtime-corejs2 @babel/runtime-corejs3
        ["@babel/plugin-transform-runtime",{
            "corejs":2
        }]
    ]
}

使用 polyfill

使用 babel-polyfill 后,可以使用内置对象如 PromiseWeakMap,静态方法如 Array.fromObject.assign,实例方法如 Array.prototypes.includes 以及 generator 函数

@babel/preset-env 拥有根据 useBuiltIns 参数的多种polyfill实现,优点是覆盖面比较全(entry), 缺点是会污染全局, 推荐在业务项目中使用

  • entry 的覆盖面积全, 但是打包体积自然就大,
  • useage 可以按需引入 polyfill, 打包体积就小, 但如果打包忽略node_modules 时如果第三方包未转译则会出现兼容问题
{
  "presets": [
    [
      "@babel/env",
      {
        "modules": false,
        "useBuiltIns": "usage"
      }
    ]
  ]
}

.eslintrc.js

module.exports = {
    extends: "standard",
    env: {
        "browser": true,
        "es6": true
    },
    rules: {
        "semi": ["error", "always"]
    }
};

gulp

const gulp = require("gulp");
const concat = require("gulp-concat");
const sourcemaps = require("gulp-sourcemaps");
const plumber = require("gulp-plumber");

const rollup = require("rollup");
// rollup plugin to convert JSON files to ES6 modules
const json = require("rollup-plugin-json");z+
//Bundle third-party dependencies in node_modules
const resolve = require("rollup-plugin-node-resolve");
//Convert CommonJS modules to ES2015
const commonjs = require("rollup-plugin-commonjs");
const babel = require("rollup-plugin-babel");
const eslint = require("rollup-plugin-eslint").eslint;
const filesize = require("rollup-plugin-filesize");

gulp.task("js", async function () {
    const bundle = await rollup.rollup({
        input: "./src/js/index.js",
        //告诉rollup不要将jquery打包,而作为外部依赖
        external: [
            "jquery",
            "crypto-js/core"
        ],
        /**
         * rollup-plugin-alias: 提供modules名称的 alias 和reslove 功能
         * rollup-plugin-babel: 提供babel能力
         * rollup-plugin-eslint: 提供eslint能力
         * rollup-plugin-node-resolve: 解析 node_modules 中的模块
         * rollup-plugin-commonjs: 转换 CJS -> ESM, 通常配合上面一个插件使用
         * rollup-plugin-serve: 类比 webpack-dev-server, 提供静态服务器能力
         * rollup-plugin-filesize: 显示 bundle 文件大小
         * rollup-plugin-uglify: 压缩 bundle 文件
         * rollup-plugin-replace: 类比 Webpack 的 DefinePlugin , 可在源码中通过 process.env.NODE_ENV 用于构建区分 Development 与 Production 环境.
         */
        plugins: [
            //rollupTypescript()
            json(),
            resolve(),
            eslint(),
            commonjs(),
            filesize(),
            babel({
                exclude: "node_modules/**",
                runtimeHelpers: true,
                plugins: []
            }),
        ]
    });

    const banner = `/*!
    * WSFileUpload 1.00.1
    * Licensed under ISC
    * Please visit https:// for details.
    */`;

    await bundle.write({
        file: "./dist/ws-fileupload.js",// 文件输出目录
        globals: {//告诉rollup 全局变量jQuery即是jquery
            "jquery": "jQuery",
            "crypto-js/core": "CryptoJS"
        },
        /**
         * amd – 异步模块定义,用于像RequireJS这样的模块加载器
         * cjs – CommonJS,适用于 Node 和 Browserify/Webpack
         * es – 将软件包保存为ES模块文件
         * iife – 一个自动执行的功能,适合作为<script>标签。(如果要为应用程序创建一个捆绑包,您可能想要使用它,因为它会使文件大小变小。)
         * umd – 通用模块定义,以amd,cjs 和 iife 为一体
         */
        format: "iife", // 格式,amd(异步模块定义),cjs(commonjs),es(将软件包保存为es模块文件),iife(适合作为<script>标签),umd(以amd、cjs、iife为一体)
        name: "WSFileUpload",//当format为iife和umd时必须提供,生成包名称,代表你的iife/umd包
        sourcemap: true,
        banner: banner,
        footer: "/* follow me on github! */",
        intro: "",
    });
});

参考

  • rollupjs
  • babel教程