前言
Rollup和Webpack
Rollup和Webpack 同样都是打包工具,那他们之间到底有什么区别呢?
Webpack
说到打包工具,大家应该第一个想到的是webpack,无论是vue还是react中都是基于Webpack打包工具进行打包,Webpack中的loader(加载器)可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。
Webpack官网
Rollup
Rollup是下一代JavaScript模块打包工具,它使用ES6的模块标准,这样不需要通过babel将import转化成Commonjs的require方式,Rollup打包的体积非常小,比起Webpack有更加使用的场景。
例如:
在CommonJS中需要引入整个完整的库,而使用ES6时,只需要引入需要使用的函数
// CommonJS
var utils = require( 'utils' );
// ES6
import { ajax } from 'utils';
因为 Rollup 只引入最基本最精简代码,所以打包生成的文件更轻量、快速。
Rollup官网
快速入门
一、新建项目
首先新建一个空文件夹,并用vscode打开
$ npm init -y //初始化项目
二、安装 Rollup
$ npm install rollup --save-dev //安装Rollup依赖
Rollup会添加到package.json文件的依赖中,并且会生成一个node_modules文件夹
三、配置 Rollup
新建一个rollup.config.js文件
-- 接下来就是rollup工具的配置了
// rollup.config.js
export default {
input: 'index.js', //入口
output: { //出口
file: 'dist/bundle.js', //文件路径
format: 'cjs' //输出格式
}
};
更多关于配置问题,可以参考官网的 Configuration Files
四、配置打包命令
-- 在package.json文件中的scripts字段中添加打包命令
// package.json
"scripts": {
"rollup":"rollup", // 查看rollup的基本配置
"build":"rollup -c", // 打包命令
"test": "echo \"Error: no test specified\" && exit 1"
},
一开始我是直接使用rollup命令去打包,然后出现了rollup的全部配置,我看了一些配置之后才知道需要添加配置, -c 的话相当于使用默认配置文件,这时候才可以进行打包,所以打包的命令是rollup -c,还有更多的配置信息,可以自己设置一个rollup命令进行查看,并且使用。执行打包命令后,rollup会根据配置文件中的信息进行对应的打包操作。
五、测试
万事俱备就差实验了,能不能行就在这一刻体现出来了,这时候我们就使用配置好的命令,执行
$ npm run build
就可以看到是否打包成功了。根据上面的配置,会新建一个dist文件夹,下面有一个打包后的文件bundle.js,可以打开bundle.js文件查看,打包后的内容是什么样的,如果代码过于简单,当然就看不出什么差别了。
PS: 对应的入口文件和出口文件都在rollup.config.js文件中设置。
小结
到这里,rollup的入门就已经结束了,学习完这些,就算是可以简单使用rollup工具进行打包了, 但由于rollup还包含非常多的其他功能,还需要去探索,所以我们会继续往下讲。
深入学习
一、多入口
基础配置是一个入口一个出口,那有没有需要打包不同版本,或者不同功能时候需要打两个或者多个包的时候呢? 答案是肯定有啊,所以也存在多入口的配置
// 单入口
export default {
input: 'index.js',
output: {
file: 'dist/bundle.js',
format: 'cjs'
}
};
// 多入口
export default {
input: {
index:'index.js',
foo:'foo.js'
},
output: {
dir: 'dist',
format: 'cjs'
}
};
只需要在入口(input)用对象的形式,将需要打包的文件引入,在出口(output)把file改成dir,并设置文件夹路径即可实现多入口配置啦。
二、Format
format是打包后的输出格式,输出格式一共有六种,分别是:amd / esm / iife / umd / cjs / system
用一个例子来看看不同输出格式打包后内容的区别:
import jq from 'jquery'
function add(a, b) {
return a + b
}
const result = add(1, 2)
console.log(result);
console.log(jq);
一、esm 格式
import 'jquery';
function add(a, b) {
return a + b
}
const result = add(1, 2);
console.log(result);
esm: ECMAScript modules -- 代码使用ES module规范,这里的import还是import
二、iife 格式
(function (jq) {
'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var jq__default = /*#__PURE__*/_interopDefaultLegacy(jq);
function add(a, b) {
return a + b
}
const result = add(1, 2);
console.log(result);
console.log(jq__default["default"]);
})(jq);
iife: 自执行函数 -- 打包后,代码变成了一个自执行函数
引入的依赖,会以参数的形式传入自执行函数,
优点:
- 有自己的作用域,不会变量污染,
- 对代码体积的影响不大
- 简单易懂 缺点:
- 输出的变量可能影响全局变量;引入依赖包时依赖全局变量。
三、 cjs 格式
'use strict';
var jq = require('jquery');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var jq__default = /*#__PURE__*/_interopDefaultLegacy(jq);
function add(a, b) {
return a + b
}
const result = add(1, 2);
console.log(result);
console.log(jq__default["default"]);
cjs: commonjs -- 代码使用commonjs规范,这里的import 打包后变成了require
优点:
- 完善的模块化方案,完美解决了
IIFE的各种缺点。 缺点: - 不支持浏览器环境,因为这种同步的引入方式可能导致浏览器假死。
四、 amd格式
define(['jquery'], (function (jq) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var jq__default = /*#__PURE__*/_interopDefaultLegacy(jq);
function add(a, b) {
return a + b
}
const result = add(1, 2);
console.log(result);
console.log(jq__default["default"]);
}));
amd: 核心内容是一个全局方法 define 。与前面那些相比会相对复杂一些,但他解决了 IIFE 和 CommonJS 所面临的问题,对“浏览器里完善的JS模块方法” 提供了一套完善的方案。
优点:
- 一套完备的浏览器里
js文件模块化方案 缺点: - 代码组织形式别扭,可读性差
五、umd 格式
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
typeof define === 'function' && define.amd ? define(['jquery'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jq));
})(this, (function (jq) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var jq__default = /*#__PURE__*/_interopDefaultLegacy(jq);
function add(a, b) {
return a + b
}
const result = add(1, 2);
console.log(result);
console.log(jq__default["default"]);
}));
umd: 整整一大段代码,只是在处理兼容性问题,判断当前应该使用 amd 或是 CommonJS。
优点:
- 抹平了一个包在
AMD和CommonJS里的差异 缺点: - 会为了兼容产生大量不好理解的代码。(理解难度与包体积)
六、system 格式
System.register(['jquery'], (function () {
'use strict';
var jq;
return {
setters: [function (module) {
jq = module["default"];
}],
execute: (function () {
function add(a, b) {
return a + b
}
const result = add(1, 2);
console.log(result);
console.log(jq);
})
};
}));
system: system 的打包结果其实和 amd 类似,提供了全局的对象 System,并提供了注册的方式和统一的写法。
三、Plugins
当我们的项目越来越大,需要引入的功能也越来越多,这时候默认的配置已经无法满足打包的需求的时候,我们就需要引入插件(plugins),来帮助打包。例如使用 Babel 编译代码,使用 JSON 文件等。
这里以引入json文件数据为例:
首先可以创建一个json文件,或者就以package.json来说,我们需要获取文件中的版本号(version)
//package.json
{
"devDependencies": {
"rollup": "^2.67.3"
},
"name": "use-rollup",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"rollup":"rollup",
"build":"rollup -c",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
第一步 先安装插件
$ npm install --save-dev @rollup/plugin-json
第二步 更新js文件内容,引入json文件数据
// index.js
import { version } from '../package.json';
console.log(version);
第三步 修改 rollup.config.js
修改前
// rollup.config.js
export default {
input:'index.js',
output: {
file: 'dist/bundle.js',
format: 'cjs'
}
};
修改后
// rollup.config.js
import json from '@rollup/plugin-json';
export default {
input: {
index:'index.js',
foo:'foo.js'
},
output: {
dir: 'dist',
format: 'cjs'
},
plugins: [json()]
};
第四步 打包
先来看看没有安装插件的情况:
会报错,报错内容是,
json is not defined
在rollup.config.js 可以看出引入了插件,在plugins处添加了插件,所以插件是一定要先安装的,否则无法识别json文件
安装完插件后,再次打包就不会再报错了,就可以识别json文件的引入了。
小结
插件的基本使用,就是打包需要引入什么插件,就安装对应插件所需的依赖,然后在plugins中添加之后进行打包即可。
总结
学习完了rollup的基本使用,接下来我会继续更新一篇rollup的实现原理的文章,和大家一起学习和分享。