Rollup完全讲解

6,278 阅读4分钟

前言,为什么要学习RollUp?因为vue框架源码就是使用Rollup进行打包的。所以需要学习。而且rollup和webpack相比更适用于框架的开发。打包后的代码可读性更高。

Rollup介绍

  • esModule打包器
  • 将项目中散落的小模块打包成整块的代码,让划分的小模块可以更好地运行在浏览器环境和nodejs环境
  • 作用: 与webpack非常类似
  • 对比: Rollup相对于webpack更小巧,webpack可以完成项目中各种工程化的需求,而Rollup仅仅是一款ESM(ESmodule)打包器,没有任何其它的功能。 例如,webpack中有HMR(热替换功能),Rollup中没有
  • Rollup的意义: 提供一个充分利用ESM各项特性的高效打包器。利用ESmodule的各种特性构建出结构比较扁平,性能比较出众的类库。

Rollup实操

一、新建项目

新建了一个超简单项目用来学习,项目中代码都是要 esmodule 规范来进行项目模块化,项目目录如下:

rollup

├─ src

├─ ─ log.js

├─ ─ sum.js

├─ ─ index.js

├─ package.json

└─ yarn.lock

//index.js
import { log } from './log.js';
import { sum } from './sum.js';

console.log('hello');
console.log(sum(2, 3));
//log.js
export function log(argu) {
    console.log(argu);
}
//sum.js
export function sum(num1, num2) {
    return num1 + num2;
}

二、安装Rollup

将Rollup作为开发依赖进行安装。

yarn add rollup --dev

安装完成后,在项目node-modules中,会有rollup.cmd命令。在命令行中可以使用rollup进行打包。

使用yarn命令可以直接去执行node-modules中bin文件夹下的命令,不用到命令目录下。

三、使用

1、尝试执行rollup

yarn rollup

没传递任何参数时会自动打印出帮助信息

use settings in config file
rollup -c

帮助信息提示:应该通过参数指定一个打包入口文件

2、配置打包入口文件

新建一个 index.js 文件

yarn rollup ./src/index

执行完上面命令,发现控制台输出打包内容。输出的内容中混入了所引入的文件的方法等。将引入的方法放在头部,文件自身的代码放至在底部。

接下来我们给rollup设置一个打包后文件的输出格式,来控制以什么格式输出。

3、配置打包后文件输出格式

在打包命令后使用 --format 配置打包格式。

yarn rollup ./src/index.js --format iife

iife为浏览器环境下可用的自执行函数格式

$ H:\xp\rollup\node_modules.bin\rollup ./src/index.js --format iife

./src/index.js → stdout...
(function () {
    'use strict';

    function log(msg) {
        console.log(msg);
    }

    var message = {
        hi: 'hello'
    };

    function sum(num1, num2) {
        return num1 + num2;
    }

    //index.js

    const hi = message.hi;
    log(hi);
    log(sum(2 + 3));

})();

4、配置打包输出文件路径

yarn rollup ./src/index.js --format iife --file dist/bundle.js

可以使用--file配置输出文件,打包结果会输出到输出文件中。

5、查看打包后输出的文件

(function () {
    'use strict';

    function log(msg) {
        console.log(msg);
    }

    var message = {
        hi: 'hello'
    };

    function sum(num1, num2) {
        return num1 + num2;
    }

    //index.js

    const hi = message.hi;
    log(hi);
    log(sum(2 + 3));

})();

查看打包输出的文件 ,打包后输出的结果非常简洁,和我们以前手写的代码是一样的,那相比于webpack当中大量的那些引导代码,还有一堆的模块函数,Rollup打包的输出结果几乎没有任何多余的代码,它就是把我们打包过程当中,各个模块按照模块的依赖顺序,先后的拼接到一起,而且输出结果当中,它只会去保留那些用到的部分。对未引用的部分都没有输出,这是因为Rollup默认会自动开启treeshaking,treeshaking最早也是在Rollup这个工具中提出的。

Rollup配置文件

Rollup同样支持以配置文件的方式去配置打包过程中的各项参数

一、项目根目录下新建配置文件

在项目的根目录下,新建一个rollup.config.js的配置文件,这个配置文件是同样运行在Node的环境当中,不过Rollup它自身会额外处理这个配置文件,所以这里我们可以直接使用ESmodule。文件中需要导出一个配置对象,在这个对象中我们可以去通过

  • input属性去指定我们打包的入口文件路径
  • output指定输出的相关配置,output格式要求是一个对象
    • 在output的对象当中,使用file属性去指定输出的文件名
    • 在output的对象当中,使用format属性用来去指定我们的输出格式。
export default {
    input: 'src/index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'iife'
    }
};

执行rollup命令,命令需要通过--config表明使用项目中配置文件,默认不会读取配置文件

yarn rollup --config

在项目中可以针对开发环境、生产环境使用不同的配置文件,例如:

yarn rollup --config rollup.dev.config.js

yarn rollup --config rollup.pro.config.js

Rollup使用插件

Rollup自身的功能仅仅只是esmodel模块的合并打包,那如果我们的项目有更高级的需求,例如

  • 想要去加载其它类型的资源文件
  • 想要在代码当中去导入commonjs模块
  • 想要Rollup去帮我们编译EMCAScript的新特性 Rollup支持使用插件的方式去扩展实现这些额外的需求,而且插件的是Rollup唯一的扩展方式,它不像webpack中划分了loader,plugin和Minimizer三种扩展方式。

rollup-plugin-json 插件

安装并导入rollup-plugin-json 插件,可以在项目中引入并使用json文件。如下示例中,安装插件后,在rollup配置文件中引入包,在index.js中可以引入 package.json 文件中的值。

一、如何在Rollup中使用插件

1、安装 rollup-plugin-json 作为开发依赖

yarn add rollup-plugin-json --dev

2、使用import的方式去导入这个插件模块。

将这个函数的调用结果添加到我们配对对象的plugins数组当中,那需要注意的是,这里我们是将调用的结果放到数组当中,而不是直接将这个函数放进去。

import json from 'rollup-plugin-json'

export default{
    input:'src/index.js',
    output:{
        file:'dist/bundle.js',
        format:''
    },
    plugins:[
        json()
    ]
}

3、在项目文件中尝试使用目前引入的插件。打开项目 src/index.js 文件

//src/index.js

import {name,version} from '../package.json'

console.log(name)
console.log(version)

执行打包命令,运行打包:

yarn rollup --config

那打包完后后,我们找到输出的bundle.js,那此时你就能够看到package.json当中的name和version已经被正常打包进来了,而json当中那些没有用到的属性,也都会被Tree shaking 掉。

(function () {
    'use strict';

    function log(msg) {
        console.log(msg);
    }

    var message = {
        hi: 'hello'
    };

    function sum(num1, num2) {
        return num1 + num2;
    }

    var name = "rollup";

    //index.js

    const hi = message.hi;

    console.log(name);
    log(hi);
    log(sum(2 + 3));

})();

Rollup加载npm模块

Rollup默认只能够按照文件路径方式去加载本地的文件模块,那对于nodd_module当中那些第三方的模块,它并不能够像webpack一样,直接去通过模块的名称导入对应的模块,那为了抹平这样一个差异,Rollup官方给出了一个rollup-plugin-node-resolve这样的插件,那通过使用这个插件,我们可以在代码当中直接去使用模块名称导入对应的模块。

1、安装插件rollup-plugin-node-resolve

yarn add rollup-plugin-node-resolve --dev

2、修改配置文件

打开配置文件,导入插件,然后将这个插件函数的调用结果配置到plugin子数组中

// rollup.config.js
import json from 'rollup-plugin-json';
import npm from 'rollup-plugin-node-resolve';
export default {
    input: 'src/index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'iife'
    },
    plugins: [json(), npm()]
};

3、安装第三方模块

这里安装lodash为例

yarn add lodash

4、导入lodash的ESmodule版本

//index.js
import { log } from './log.js';
import message from './message';
import { sum } from './sum.js';
import { name, version } from '../package.json';

// 使用刚安装好的lodash模块
import _ from 'lodash-es';

const hi = message.hi;

let arr1 = _.first([2, 3, 4]);
console.log(name);
log(hi);
log(sum(2 + 3));

导入过后呢,我们就可以使用这个模块所提供的一些工具方法了

4、打开命令行终端,运行rollup进行打包

yarn rollup --config

lodash中的代码就能够被打包到我们的boundle.js中了,这里引入lodash的ESmodule版本的原因是因为rollup它默认只能够ESmodule模块,如果说需要去使用普通版本,需要做额外的处理。

//dist/bundle.js
(function (_) {
    'use strict';

    function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

    var ___default = /*#__PURE__*/_interopDefaultLegacy(_);

    function log(msg) {
        console.log(msg);
    }

    var message = {
        hi: 'hello'
    };

    function sum(num1, num2) {
        return num1 + num2;
    }

    var name = "rollup";

    //index.js

    const hi = message.hi;

    ___default["default"].first([2, 3, 4]);
    console.log(name);
    log(hi);
    log(sum(2 + 3));

})(_);

Rollup加载CommonJS模块

Rollup设计的就是只处理ESmodule模块打包,那如果我们在代码当中去导入commonJSl模块,默认是不被支持的,但是目前还是会有大量的NPM模块使用commonJS方式去导出成员,所以为了兼容这些模块,官方又给出了一个rollup-plugin-commonjs插件。

使用方法

一、安装rollup-plugin-commonjs插件

yarn add rollup-plugin-commonjs --dev

二、修改配置文件

打开rollup打包配置文件,导入rollup-plugin-commonjs插件,在pliguns数组中加入插件函数的执行结果。

// rollup.config.js

import json from 'rollup-plugin-json';
import npm from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
export default {
    input: 'src/index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'iife'
    },
    plugins: [json(), npm(), commonjs()]
};

三、插件CommonJs模块文件并使用

目录中创建 cjs-module.js文件,项目中添加一个Commonjs模块文件。 \

//cjs-module.js
module.exports={
    foo:'bar'
}

在index.js中导入这个模块,

//index.js
import { log } from './log.js';
import message from './message';
import { sum } from './sum.js';
import { name, version } from '../package.json';

// 使用刚安装好的lodash模块
import _ from 'lodash-es';
// 引入commonjs模块.commonJs模块默认为以export.default形式导出
import cjs from './cjs-module';
const hi = message.hi;

let arr1 = _.first([2, 3, 4]);
log(name);
log(cjs);
log(hi);
log(sum(2 + 3));

四、执行rollup打包

yarn rollup --config

(function (_) {
    'use strict';

    function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

    var ___default = /*#__PURE__*/_interopDefaultLegacy(_);

    function log(msg) {
        console.log(msg);
    }

    var message = {
        hi: 'hello'
    };

    function sum(num1, num2) {
        return num1 + num2;
    }

    var name = "rollup";

    //cjs-module.js
    var cjsModule = {
        foo: 'bar'
    };
    cjsModule.foo;

    //index.js
    const hi = message.hi;

    ___default["default"].first([2, 3, 4]);
    log(name);
    log(cjsModule);
    log(hi);
    log(sum(2 + 3));

})(_);

Rollup 代码拆分(Code Splitting)

在Rollup最新的版本中,已经开始支持代码拆分了,那么同样可以使用符合ESmodule标准的动态导入的方式去实现模块的按需加载,Rollup内部也会自动去处理代码的拆分(也就是分包)。

一、回到打包入口文件当中

//index.js
// import动态导入方法返回一个promise。then方法中可以取到模块导入后的对象。这里解构提取出 log方法
import('./log').then(({ log }) => {
    log('hello');
});

然后我们使用动态导入方式去导入一下 logger.js 对应的这个模块,import()方法返回的是一个promise对象,在它的.then( )方法中我们可以拿到一个模块导入过后的对象,模块导出的那些成员都会放在这对象当中,所以可以使用解构的方式去提取出来里面的log方法,这里可以调用log()方法,

尝试运行 rollup 进行打包

yarn rollop --config

src/index2.js → dist/bundle.js...
[!] Error: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
https://rollupjs.org/guide/en/#outputformat
Error: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
    at error (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:158:30)
    at validateOptionsForMultiChunkOutput (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:16109:16)
    at Bundle.generate (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:15948:17)
    at async handleGenerateWrite (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:23480:23)
    at async Promise.all (index 0)
    at async build (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1576:5)
    at async runRollup (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1730:21)

error Command failed with exit code 1.

会报出一个错误,说的是我们使用Code Splitting的输出格式,不能是IIFE自执行函数这种形式,原因很简单,因为自执行函数,它会把所有的模块都放到同一个函数当中,它并没有像webpack一样有一些引导代码,所以说它没有办法实现代码拆分,那想使用代码拆分的话,就必须要使用AMD或者是Commonjs这样的一些其它的标准,那我们在浏览器还当中,我们只能使用AMD标准,所以我们这里需要使用amd的格式去输出打包结果

重新执行打包命令,修改打包文件输出格式,通过--format参数去覆盖配置文件当中的format设置,把它设置为amd。这里在命令中使用--format amd 覆盖配置文件中iife 打包格式。

yarn rollop --config --format amd

这里同样报出了一个错误,说的是我们Code Splitting这种方式,它需要输出多个文件,因为需要输出多个文件,我们这儿呢就不能再使用file的这种配置方式,因为file它是指定我们一个单子文件输出的文件的文件名,那我们如果需要输出多个文件的话,我们可以使用dir的参数

$ H:\xp\rollup\node_modules.bin\rollup --config --format amd

src/index2.js → dist/bundle.js...
[!] Error: Invalid value for option "output.file" - when building multiple chunks, the "output.dir" option must be used, not "output.file". To inline dynamic imports, set 
the "inlineDynamicImports" option.
https://rollupjs.org/guide/en/#outputdir
Error: Invalid value for option "output.file" - when building multiple chunks, the "output.dir" option must be used, not "output.file". To inline dynamic imports, set the 
"inlineDynamicImports" option.
    at error (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:158:30)
    at validateOptionsForMultiChunkOutput (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:16111:16)
    at Bundle.generate (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:15948:17)
    at async handleGenerateWrite (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:23480:23)
    at async Promise.all (index 0)
    at async build (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1576:5)
    at async runRollup (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1730:21)

error Command failed with exit code 1.

修改rollup打包配置文件

export default{
    input:'src/index.js',
    output:{
        //file:'dist/bundle.js',
        //format:'IIFE'
        
        dir:'dist',
        format:'amd'
    }
}

执行打包命令,在执行打包命令前删除dist文件夹下所有文件,等下观察分包打包后输出文件

yarn rollup --config

dist 文件夹下成两个文件。

//index.js
define(['require'], (function (require) { 'use strict';

    // import动态导入方法返回一个promise。then方法中可以取到模块导入后的对象。这里解构提取出 log方法
    new Promise(function (resolve, reject) { require(['./log-ace3f028'], resolve, reject); }).then(({ log }) => {
        log('hello');
    });

}));
//log-ace3f028.js
define(['exports'], (function (exports) { 'use strict';

    function log(msg) {
        console.log(msg);
    }

    function error(msg) {
        console.error(msg);
    }

    exports.error = error;
    exports.log = log;

}));

Rollup 多入口打包

支持多入口打包。

对于不同入口于文件中公共部分也会自动提取到单个文件中作为bundle。

配置多入口,将input格式设置为数组,放入多个打包入口文件。

export default{
    input:['src/index.js','src/']
    output:{
        //file:'dist/bundle.js',
        //format:'IIFE'
        
        dir:'dist',
        format:'amd'
    }
}

不能使用IIFE输出格式,需要将输出格式改为amd

执行打包,打包后会生成,不同的入口文件,及公共提取的公共模块

不能直接用到页面上,需要使用amd的库加载。

使用requireJs引入amd模块

<script src=''  data-main=''></script>

Rollup和webpack如何选

  • 输出结果更加扁平,效率高
  • 自动移除未引用代码,tree shaking
  • 打包结果依然完全可读

缺点:

  • 加载非ESM的第三方模块比较复杂\
  • 模块最终都被打包到一个函数中,无法实现HMR\
  • 浏览器环境中,代码拆分功能依赖AMD库

如果我们正在开发应用程序,rollup欠缺。

如果我们正在开发一个框架或者类库,大多数知名框架/库都在使用Rollup。