上一篇教程中,为你们介绍了rollup.js的入门技巧,没有读过的小伙伴能够点击这里,本次咱们将继续对rollup.js的进阶技巧进行探讨,想直接看结论的小伙伴能够直接看最后一章。node
rollup.js的插件采用可拔插设计,它帮助咱们加强了rollup.js的基础功能,下面我将重点讲解四个rollup.js最经常使用的插件。webpack
上一篇教程中,咱们打包的对象是本地的js代码和库,但实际开发中,不太可能全部的库都位于本地,咱们会经过npm下载远程的库。这里我专门准备了一些测试库,供你们学习rollup.js使用,首先下载测试库:es6
npm i -S sam-test-data
复制代码
sam-test-data库默认提供了一个UMD模块,对外暴露了两个变量a和b以及一个random函数,a是0到9之间的一个随机整数,b是0到99之间的一个随机整数,random函数的参数是一个整数,如传入100,则返回一个0到99之间的随机整数,在本地建立测试插件代码的文件夹:web
mkdir src/plugin
复制代码
建立测试代码:npm
touch src/plugin/main.js
复制代码
写入如下代码:json
import * as test from 'sam-test-data'
console.log(test)
export default test.random
复制代码
先不使用rollup.js打包,直接经过babel-node尝试运行代码:api
babel-node
> require('./src/plugin/main.js')
{ a: 1, b: 17, random: [Function: random] }
{ default: [Function: random] }
> require('./src/plugin/main.js').default(100)
41
复制代码
能够看到代码能够正常运行,下面咱们尝试经过rollup.js打包代码,添加一个新的配置文件:浏览器
touch rollup.plugin.config.js
复制代码
写入如下内容:bash
import { comment } from './comment-helper-es'
export default {
input: './src/plugin/main.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs',
banner: comment('welcome to imooc.com', 'this is a rollup test project'),
footer: comment('powered by sam', 'copyright 2018')
}, {
file: './dist/index-plugin-es.js',
format: 'es',
banner: comment('welcome to imooc.com', 'this is a rollup test project'),
footer: comment('powered by sam', 'copyright 2018')
}]
}
复制代码
这里我提供了一个comment-helper-es模块,暴露了一个comment方法,自动读取咱们的参数,并帮助生成注释,同时会在注释上方和下方添加等长的分隔符,感兴趣的小伙伴能够直接拿去用:babel
export function comment() {
if (arguments.length === 0) {
return // 若是参数为0直接返回
}
let maxlength = 0
for (let i = 0; i < arguments.length; i++) {
const length = arguments[i].toString().length
maxlength = length > maxlength ? length : maxlength // 获取最长的参数
}
maxlength = maxlength === 0 ? maxlength : maxlength + 1 // 在最长参数长度上再加1,为了美观
let seperator = ''
for (let i = 0; i < maxlength; i++) {
seperator += '=' // 根据参数长度生成分隔符
}
const c = []
c.push('/**\n') // 添加注释头
c.push(' * ' + seperator + '\n') // 添加注释分隔符
for (let i = 0; i < arguments.length; i++) {
c.push(' * ' + arguments[i] + '\n') // 加入参数内容
}
c.push(' * ' + seperator + '\n') // 添加注释分隔符
c.push(' **/') // 添加注释尾
return c.join('') // 合并参数为字符串
}
复制代码
经过rollup.js打包:
$ rollup -c rollup.plugin.config.js
./src/plugin/main.js → ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js...
(!) Unresolved dependencies
https://rollupjs.org/guide/en#warning-treating-module-as-external-dependency
sam-test-data (imported by src/plugin/main.js)
created ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js in 13ms
复制代码
能够看到代码生成成功了,可是sam-test-data被当作一个外部的模块被引用,咱们查看dist/index-plugin-es.js源码:
/** * ============================== * welcome to imooc.com * this is a rollup test project * ============================== **/
import * as test from 'sam-test-data';
import { random } from 'sam-test-data';
console.log(test);
var main = random;
export default main;
/** * =============== * powered by sam * copyright 2018 * =============== **/
复制代码
和咱们本来写的代码几乎没有区别,只是经过es6的解构赋值将random函数单独从sam-test-data获取,而后赋给变量main并暴露出来。你们试想,若是咱们正在编写一个Javascript类库,用户在引用咱们库的时候,还须要手动去下载这个库全部的依赖,这是多么糟糕的体验。为了解决这个问题,将咱们编写的源码与依赖的第三方库进行合并,rollup.js为咱们提供了resolve插件。
首先,安装resolve插件:
npm i -D rollup-plugin-node-resolve
复制代码
修改配置文件rollup.plugin.config.js:
import resolve from 'rollup-plugin-node-resolve'
export default {
input: './src/plugin/main.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve()
]
}
复制代码
从新打包:
$ rollup -c rollup.plugin.config.js
./src/plugin/main.js → ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js...
created ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js in 28ms
复制代码
能够看到警告消除了,咱们从新查看dist/index-plugin-es.js源码:
const a = Math.floor(Math.random() * 10);
const b = Math.floor(Math.random() * 100);
function random(base) {
if (base && base % 1 === 0) {
return Math.floor(Math.random() * base)
} else {
return 0
}
}
var test = /*#__PURE__*/Object.freeze({
a: a,
b: b,
random: random
});
console.log(test);
var main = random;
export default main;
复制代码
很明显sam-test-data库的源码已经与咱们的源码集成了。
下面咱们修改src/plugin/main.js的源码:
import * as test from 'sam-test-data'
export default test.random
复制代码
源码中去掉了console.log(test)
,从新打包:
rollup -c rollup.plugin.config.js
复制代码
再次查看dist/index-plugin-es.js源码:
function random(base) {
if (base && base % 1 === 0) {
return Math.floor(Math.random() * base)
} else {
return 0
}
}
var main = random;
export default main;
复制代码
咱们发现关于变量a和b的定义没有了,由于源码中并无用到这两个变量。这就是ES模块著名的tree-shaking机制,它动态地清除没有被使用过的代码,使得代码更加精简,从而可使得咱们的类库得到更快的加载速度(容量小了,天然加载速度变快)。
有些场景下,虽然咱们使用了resolve插件,但咱们仍然某些库保持外部引用状态,这时咱们就须要使用external属性,告诉rollup.js哪些是外部的类库,修改rollup.js的配置文件:
import resolve from 'rollup-plugin-node-resolve'
export default {
input: './src/plugin/main.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve()
],
external: ['sam-test-data']
}
复制代码
从新打包:
rollup -c rollup.plugin.config.js
复制代码
查看dist/index-plugin-es.js源码:
import { random } from 'sam-test-data';
var main = random;
export default main;
复制代码
能够看到虽然使用了resolve插件,sam-test-data库仍被当作外部库处理
rollup.js默认不支持CommonJS模块,这里我编写了一个CommonJS模块用于测试,该模块的内容与sam-test-data彻底一致,差别仅仅是前者采用了CommonJS规范,先安装这个模块:
npm i -S sam-test-data-cjs
复制代码
新建一个代码文件:
touch src/plugin/main-cjs.js
复制代码
写入以下代码:
import test from 'sam-test-data-cjs'
console.log(test)
export default test.random
复制代码
这段代码很是简单,接下来修改rollup.js的配置文件:
import resolve from 'rollup-plugin-node-resolve'
export default {
input: './src/plugin/main-cjs.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve()
]
}
复制代码
执行打包:
rollup -c rollup.plugin.config.js
./src/plugin/main-cjs.js → ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js...
[!] Error: 'default' is not exported by node_modules/_sam-test-data-cjs@0.0.1@sam-test-data-cjs/index.js
https://rollupjs.org/guide/en#error-name-is-not-exported-by-module-
src/plugin/main-cjs.js (1:7)
1: import test from 'sam-test-data-cjs'
^
复制代码
能够看到默认状况下,rollup.js是没法识别CommonJS模块的,此时咱们须要借助commonjs插件来解决这个问题。
首先安装commonjs插件:
npm i -D rollup-plugin-commonjs
复制代码
修改rollup.js的配置文件:
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
export default {
input: './src/plugin/main-cjs.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve(),
commonjs()
]
}
复制代码
从新执行打包:
rollup -c rollup.plugin.config.js
复制代码
打包成功后,咱们查看dist/index-plugin-es.js源码:
const a = Math.floor(Math.random() * 10);
const b = Math.floor(Math.random() * 100);
function random(base) {
if (base && base % 1 === 0) {
return Math.floor(Math.random() * base)
} else {
return 0
}
}
var _samTestDataCjs_0_0_1_samTestDataCjs = {
a, b, random
};
console.log(_samTestDataCjs_0_0_1_samTestDataCjs);
var mainCjs = _samTestDataCjs_0_0_1_samTestDataCjs.random;
export default mainCjs;
复制代码
能够看到CommonJS模块被集成到代码中了,经过babel-node尝试执行打包后的代码:
babel-node
> require('./dist/index-plugin-es')
{ a: 7, b: 45, random: [Function: random] }
{ default: [Function: random] }
> require('./dist/index-plugin-es').default(1000)
838
复制代码
代码执行成功,说明咱们的代码打包成功了。
咱们修改src/plugin/main-cjs.js的源码,验证一下CommonJS模块是否支持tree-shaking特性:
import test from 'sam-test-data-cjs'
export default test.random
复制代码
与resolve中tree-shaking的案例同样,咱们去掉console.log(test)
,从新执行打包后,再查看打包源码:
const a = Math.floor(Math.random() * 10);
const b = Math.floor(Math.random() * 100);
function random(base) {
if (base && base % 1 === 0) {
return Math.floor(Math.random() * base)
} else {
return 0
}
}
var _samTestDataCjs_0_0_1_samTestDataCjs = {
a, b, random
};
var mainCjs = _samTestDataCjs_0_0_1_samTestDataCjs.random;
export default mainCjs;
复制代码
能够看到源码中仍然定义了变量a和b,说明CommonJS模块不能支持tree-shaking特性,因此建议你们使用rollup.js打包时,尽可能使用ES模块,以得到更精简的代码。
UMD模块与CommonJS相似,也是不可以支持tree-shaking特性的,这里我提供了一个UMD测试模块sam-test-data-umd,感兴趣的小伙伴能够本身验证一下。有的小伙伴可能会问,sam-test-data也是一个UMD模块,为何它可以支持tree-shaking?咱们打开sam-test-data的package.json一探究竟:
{
"name": "sam-test-data",
"version": "0.0.4",
"description": "provide test data",
"main": "dist/sam-test-data.js",
"module": "dist/sam-test-data-es.js"
}
复制代码
能够看到main属性指向dist/sam-test-data.js,这是一个UMD模块,可是module属性指向dist/sam-test-data-es.js,这是一个ES模块,rollup.js默认状况下会优先寻找并加载module属性指向的模块。因此sam-test-data的ES模块被优先加载,从而可以支持tree-shaking特性。咱们看一下rollup.js官方的说明:
在 package.json 文件的 main 属性中指向当前编译的版本。若是你的 package.json 也具备 module 字段,像 Rollup 和 webpack 2 这样的 ES6 感知工具(ES6-aware tools)将会直接导入 ES6 模块版本。
在src/plugin目录下建立一个新文件main-es.js:
touch src/plugin/main-es.js
复制代码
写入以下代码:
import { a, b, random } from 'sam-test-data-es'
console.log(a, b, random)
export default (base) => {
return random(base)
}
复制代码
代码中采用了ES6的新特性:箭头函数,修改配置文件:
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
export default {
input: './src/plugin/main-es.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve(),
commonjs()
]
}
复制代码
从新执行打包:
rollup -c rollup.plugin.config.js
复制代码
查看dist/index-plugin-es.js源码:
var mainEs = (base) => {
return random(base)
};
export default mainEs;
复制代码
能够看到箭头函数被保留下来,这样的代码在不支持ES6的环境下将没法运行。咱们指望在rollup.js打包的过程当中就能使用babel完成代码转换,所以咱们须要babel插件。
首先安装babel插件:
npm i -D rollup-plugin-babel
复制代码
修改配置文件,增长babel插件的引用:
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import babel from 'rollup-plugin-babel'
export default {
input: './src/plugin/main-es.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve(),
commonjs(),
babel()
]
}
复制代码
从新打包:
rollup -c rollup.plugin.config.js
复制代码
再次查看dist/index-plugin-es.js源码:
var mainEs = (function (base) {
return random(base);
});
export default mainEs;
复制代码
能够看到箭头函数被转换为了function,babel插件正常工做。
在src/plugin下建立一个新文件main-json.js:
touch src/plugin/main-json.js
复制代码
把package.json当作一个模块来引入,并打印package.json中的name和main属性:
import json from '../../package.json'
console.log(json.name, json.main)
复制代码
使用bable-node尝试执行main-json.js:
$ babel-node src/plugin/main-json.js
rollup-test index.js
复制代码
能够看到name和main字段都被打印出来了,babel-node能够正确识别json模块。下面修改rollup.js的配置文件:
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import babel from 'rollup-plugin-babel'
export default {
input: './src/plugin/main-json.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve(),
commonjs(),
babel()
]
}
复制代码
从新打包:
$ rollup -c rollup.plugin.config.js
./src/plugin/main-json.js → ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js...
[!] Error: Unexpected token (Note that you need rollup-plugin-json to import JSON files)
复制代码
能够看到默认状况下rollup.js不支持导入json模块,因此咱们须要使用json插件来支持。
下载json插件:
npm i -D rollup-plugin-json
复制代码
修改配置文件:
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import babel from 'rollup-plugin-babel'
import json from 'rollup-plugin-json'
export default {
input: './src/plugin/main-json.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}, {
file: './dist/index-plugin-es.js',
format: 'es'
}],
plugins: [
resolve(),
commonjs(),
babel(),
json()
]
}
复制代码
从新打包:
rollup -c rollup.plugin.config.js
复制代码
查看dist/index-plugin-cjs.js源码,能够看到json文件被解析为一个对象进行处理:
var name = "rollup-test";
var version = "1.0.0";
var description = "";
var main = "index.js";
var scripts = {
test: "echo \"Error: no test specified\" && exit 1"
};
var author = "";
var license = "ISC";
var devDependencies = {
"@babel/core": "^7.1.6",
"@babel/plugin-external-helpers": "^7.0.0",
"@babel/preset-env": "^7.1.6",
rollup: "^0.67.3",
"rollup-plugin-babel": "^4.0.3",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-node-resolve": "^3.4.0"
};
var dependencies = {
epubjs: "^0.3.80",
loadsh: "^0.0.3",
"sam-test-data": "^0.0.4",
"sam-test-data-cjs": "^0.0.1",
"sam-test-data-es": "^0.0.1",
"sam-test-data-umd": "^0.0.1"
};
var json = {
name: name,
version: version,
description: description,
main: main,
scripts: scripts,
author: author,
license: license,
devDependencies: devDependencies,
dependencies: dependencies
};
console.log(json.name, json.main);
复制代码
uglify插件能够帮助咱们进一步压缩代码的体积,首先安装插件:
npm i -D rollup-plugin-uglify
复制代码
修改rollup.js的配置文件:
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import babel from 'rollup-plugin-babel'
import json from 'rollup-plugin-json'
import { uglify } from 'rollup-plugin-uglify'
export default {
input: './src/plugin/main.js',
output: [{
file: './dist/index-plugin-cjs.js',
format: 'cjs'
}],
plugins: [
resolve(),
commonjs(),
babel(),
json(),
uglify()
]
}
复制代码
这里要注意的是uglify插件不支持ES模块和ES6语法,因此只能打包成非ES格式的代码,若是碰到ES6语法则会出现报错:
$ rollup -c rollup.plugin.config.js
./src/plugin/main.js → ./dist/index-plugin-cjs.js, ./dist/index-plugin-es.js...
19 | var main = random;
20 |
> 21 | export default main;
| ^ Unexpected token: keyword (default)
[!] (uglify plugin) Error: Unexpected token: keyword (default)
复制代码
因此这里咱们采用sam-test-data进行测试,由于这个模块采用了babel进行编译,其余几个模块uglify都不支持(由于其余几个模块使用了const,const也是ES6特性,uglify不能支持),因此你们在本身编写类库的时候要注意使用babel插件进行编译。配置完成后从新打包:
$ rollup -c rollup.plugin.config.js
./src/plugin/main.js → ./dist/index-plugin-cjs.js...
created ./dist/index-plugin-cjs.js in 679ms
复制代码
查看dist/index-plugin-cjs.js源码:
"use strict";var a=Math.floor(10*Math.random()),b=Math.floor(100*Math.random());function random(a){return a&&a%1==0?Math.floor(Math.random()*a):0}var test=Object.freeze({a:a,b:b,random:random});console.log(test);var main=random;module.exports=main;
复制代码
能够看到代码被最小化了,体积也减少了很多。
rollup.js的watch模式支持监听代码变化,一旦修改代码后将自动执行打包,很是方便,使用方法是在打包指令后添加--watch
便可:
$ rollup -c rollup.plugin.config.js --watch
rollup v0.67.1
bundles ./src/plugin/main-json.js → dist/index-plugin-cjs.js, dist/index-plugin-es.js...
created dist/index-plugin-cjs.js, dist/index-plugin-es.js in 24ms
[2018-11-20 22:26:24] waiting for changes...
复制代码
rollup.js支持咱们经过API来启动watch模式,在项目根目录下建立如下文件:
为了让node可以执行咱们的程序,因此采用CommonJS规范,rollup-watch-input-options.js代码以下:
const json = require('rollup-plugin-json')
const resolve = require('rollup-plugin-node-resolve')
const commonjs = require('rollup-plugin-commonjs')
const babel = require('rollup-plugin-babel')
const uglify = require('rollup-plugin-uglify').uglify
module.exports = {
input: './src/plugin/main.js',
plugins: [
json(),
resolve({
customResolveOptions: {
moduleDirectory: 'node_modules' // 仅处理node_modules内的库
}
}),
babel({
exclude: 'node_modules/**' // 排除node_modules
}),
commonjs(),
uglify() // 代码压缩
]
}
复制代码
rollup-watch-output-options.js代码以下:
module.exports = [{
file: './dist/index-cjs.js',
format: 'cjs',
name: 'sam-cjs'
}]
复制代码
rollup-watch-options.js代码以下:
module.exports = {
include: 'src/**', // 监听的文件夹
exclude: 'node_modules/**' // 排除监听的文件夹
}
复制代码
rollup-watch.js代码以下:
const rollup = require('rollup')
const inputOptions = require('./rollup-watch-input-options')
const outputOptions = require('./rollup-watch-output-options')
const watchOptions = require('./rollup-watch-options')
const options = {
...inputOptions,
output: outputOptions,
watchOptions
} // 生成rollup的options
const watcher = rollup.watch(options) // 调用rollup的api启动监听
watcher.on('event', event => {
console.log('从新打包中...', event.code)
}) // 处理监听事件
// watcher.close() // 手动关闭监听
复制代码
经过node直接启动监听:
$ node rollup-watch.js
从新打包中... START
从新打包中... BUNDLE_START
从新打包中... BUNDLE_END
从新打包中... END
复制代码
以后咱们再修改src/plugin/main.js的源码,rollup.js就会自动对代码进行打包。
本教程详细讲解了rollup.js的插件、tree-shaking机制和watch模式,涉及知识点整理以下: