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