上一篇教程中,爲你們介紹了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模式,涉及知識點整理以下: