一篇文章帶你從零快速上手Rollup
前言
專案中一直用的都是webpack,前一段需要開發幾個類庫供其他平臺使用,本來打算繼續用webpack的,但感覺webpack用來開發js庫,不僅繁瑣而且打包後的檔案體積也比較大。正好之前看vue原始碼,知道vue也是通過rollup打包的。這次又是開發類庫的,於是就快速上手了rollup。
本篇文章是我有了一定的專案實踐後,回過來給大家分享一下如何從零快速上手rollup。
什麼是rollup?
系統的瞭解rollup之前,我們先來簡單瞭解下What is rollup?
關於rollup的介紹,官方文件已經寫的很清楚了:
Rollup 是一個 JavaScript 模組打包器,可以將小塊程式碼編譯成大塊複雜的程式碼,例如 library 或應用程式。
與Webpack偏向於應用打包的定位不同,rollup.js更專注於Javascript類庫打包。
我們熟知的Vue、React等諸多知名框架或類庫都是通過rollup.js進行打包的。
為什麼是rollup?
webpack我相信做前端的同學大家都用過,那麼為什麼有些場景還要使用rollup呢?這裡我簡單對webpack和rollup做一個比較:
總體來說webpack和rollup在不同場景下,都能發揮自身優勢作用。webpack對於程式碼分割和靜態資源匯入有著“先天優勢”,並且支援熱模組替換(HMR),而rollup並不支援。
所以當開發應用時可以優先選擇webpack,但是rollup對於程式碼的Tree-shaking和ES6模組有著演算法優勢上的支援,若你專案只需要打包出一個簡單的bundle包,並是基於ES6模組開發的,可以考慮使用rollup。
其實webpack從2.0開始就已經支援Tree-shaking,並在使用babel-loader的情況下還可以支援es6 module的打包。實際上,rollup已經在漸漸地失去了當初的優勢了。但是它並沒有被拋棄,反而因其簡單的API、使用方式被許多庫開發者青睞,如React、Vue等,都是使用rollup作為構建工具的。
快速上手
我們先花大概十分鐘左右的時間來了解下rollup的基本使用以及完成一個hello world。
安裝
首先全域性安裝rollup:
npm i rollup -g
目錄準備(hello world)
接著,我們初始化一個如下所示的專案目錄
├── dist # 編譯結果
├── example # HTML引用例子
│ └── index.html
├── package.json
└── src # 原始碼
└── index.js
首先我們在src/index.js中寫入如下程式碼:
console.log("柯森");
然後在命令列執行以下命令:
rollup src/index.js -f umd -o dist/bundle.js
執行命令,我們即可在dist目錄下生成bundle.js檔案:
(function (factory) { typeof define === 'function' && define.amd ? define(factory) : factory(); }((function () { 'use strict'; console.log("柯森"); })));
這時,我們再在example/index.html中引入上面打包生成的bundle.js檔案,開啟瀏覽器:
如我們所預料的,控制檯輸出了柯森。
到這裡,我們就用rollup打包了一個最最簡單的demo。
可能很多同學看到這裡對於上面命令列中的引數不是很明白,我依次說明下:
- -f。-f引數是--format的縮寫,它表示生成程式碼的格式,amd表示採用AMD標準,cjs為CommonJS標準,esm(或 es)為ES模組標準。-f的值可以為amd、cjs、system、esm('es'也可以)、iife或umd中的任何一個。
- -o。-o指定了輸出的路徑,這裡我們將打包後的檔案輸出到dist目錄下的bundle.js
其實除了這兩個,還有很多其他常用的命令(這裡我暫且列舉剩下兩個也比較常用的,完整的rollup 命令列引數):
- -c。指定rollup的配置檔案。
- -w。監聽原始檔是否有改動,如果有改動,重新打包。
使用配置檔案(rollup.config.js)
使用命令列的方式,如果選項少沒什麼問題,但是如果新增更多的選項,這種命令列的方式就顯得麻煩了。
為此,我們可以建立配置檔案來囊括所需的選項
在專案中建立一個名為rollup.config.js的檔案,增加如下程式碼:
export default { input: ["./src/index.js"],output: { file: "./dist/bundle.js",format: "umd",name: "experience",},};
然後命令列執行:
rollup -c
開啟dist/bundle.js檔案,我們會發現和上面採用命令列的方式打包出來的結果是一樣的。
這裡,我對配置檔案的選項做下簡單的說明:
- input表示入口檔案的路徑(老版本為 entry,已經廢棄)
- output表示輸出檔案的內容,它允許傳入一個物件或一個數組,當為陣列時,依次輸出多個檔案,它包含以下內容:
- output.file:輸出檔案的路徑(老版本為 dest,已經廢棄)
- output.format:輸出檔案的格式
- output.banner:檔案頭部新增的內容
- output.footer:檔案末尾新增的內容
到這裡,相信你已經差不多上手rollup了。
進階
但是,這對於真實的業務場景是遠遠不夠的。
下面,我將介紹rollup中的幾種常用的外掛以及external屬性、tree-shaking機制。
resolve外掛
為什麼要使用resolve外掛
在上面的入門案例中,我們打包的物件是本地的js程式碼和庫,但實際開發中,不太可能所有的庫都位於本地,我們大多會通過npm下載遠端的庫。
與webpack和browserify這樣的其他捆綁包不同,rollup不知道如何打破常規去處理這些依賴。因此我們需要新增一些配置。
resolve外掛使用
首先在我們的專案中新增一個依賴the-answer,然後修改src/index.js檔案:
import answer from "the-answer"; export default function () { console.log("the answer is " + answer); }
執行npm run build
。
這裡為了方便,我將原本的rollup -c -w新增到了package.json的scripts中:"build": "rollup -c -w"
會得到以下報錯:
打包後的bundle.js仍然會在Node.js中工作,但是the-answer不包含在包中。為了解決這個問題,將我們編寫的原始碼與依賴的第三方庫進行合併,rollup.js為我們提供了resolve外掛。
首先,安裝resolve外掛:
npm i -D @rollup/plugin-node-resolve
修改配置檔案rollup.config.js:
import resolve from "@rollup/plugin-node-resolve"; export default { input: ["./src/index.js"],plugins: [resolve()],};
這時再次執行npm run build,可以發現報錯已經沒有了:
開啟dist/bundle.js檔案:
(function (global,factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self,global.experience = factory()); }(this,(function () { 'use strict'; var index = 42; function index$1 () { console.log("the answer is " + index); } return index$1; })));
打包檔案bundle.js中已經包含了引用的模組。
有些場景下,雖然我們使用了resolve外掛,但可能我們仍然想要某些庫保持外部引用狀態,這時我們就需要使用external屬性,來告訴rollup.js哪些是外部的類庫。
external 屬性
修改rollup.js的配置檔案:
import resolve from "@rollup/plugin-node-resolve"; export default { input: ["./src/index.js"],external: ["the-answer"],};
重新打包,開啟dist/bundle.js檔案:
(function (global,factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('the-answer')) : typeof define === 'function' && define.amd ? define(['the-answer'],factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self,global.experience = factory(global.answer)); }(this,(function (answer) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var answer__default = /*#__PURE__*/_interopDefaultLegacy(answer); function index () { console.log("the answer is " + answer__default['default']); } return index; })));
這時我們看到the-answer已經是做為外部庫被引入了。
commonjs外掛
為什麼需要commonjs外掛
rollup.js編譯原始碼中的模組引用預設只支援 ES6+的模組方式import/export。然而大量的npm模組是基於CommonJS模組方式,這就導致了大量 npm模組不能直接編譯使用。
因此使得rollup.js編譯支援npm模組和CommonJS模組方式的外掛就應運而生:@rollup/plugin-commonjs。
commonjs外掛使用
首先,安裝該模組:
npm i -D @rollup/plugin-commonjs
然後修改rollup.config.js檔案:
import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; export default { input: ["./src/index.js"],plugins: [resolve(),commonjs()],};
babel外掛
為什麼需要babel外掛?
我們在src目錄下新增es6.js檔案(⚠️ 這裡我們使用了 es6 中的箭頭函式):
const a = 1; const b = 2; console.log(a,b); export default () => { return a + b; };
然後修改rollup.config.js配置檔案:
import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; export default { input: ["./src/es6.js"],output: { file: "./dist/esBundle.js",};
執行打包,可以看到dist/esBundle.js檔案內容如下:
(function (global,(function () { 'use strict'; const a = 1; const b = 2; console.log(a,b); var es6 = () => { return a + b; }; return es6; })));
可以看到箭頭函式被保留下來,這樣的程式碼在不支援ES6的環境下將無法執行。我們期望在rollup.js打包的過程中就能使用babel完成程式碼轉換,因此我們需要babel外掛。
babel外掛的使用
首先,安裝:
npm i -D @rollup/plugin-babel
同樣修改配置檔案rollup.config.js:
import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import babel from "@rollup/plugin-babel"; export default { input: ["./src/es6.js"],commonjs(),babel()],};
然後打包,發現會出現報錯:
提示我們缺少@babel/core,因為@babel/core是babel的核心。我們來進行安裝:
npm i @babel/core
再次執行打包,發現這次沒有報錯了,但是我們嘗試開啟dist/esBundle.js:
(function (global,b); var es6 = (() => { return a + b; }); return es6; })));
可以發現箭頭函式仍然存在,顯然這是不正確的,說明我們的babel外掛沒有起到作用。這是為什麼呢?
原因是由於我們缺少.babelrc檔案,新增該檔案:
{ "presets": [ [ "@babel/preset-env",{ "modules": false,// "useBuiltIns": "usage" } ] ] }
我們看.babelrc配置了preset env,所以先安裝這個外掛:
npm i @babel/preset-env
這次再次執行打包,我們開啟dist/esBundle.js檔案:
(function (global,(function () { 'use strict'; var a = 1; var b = 2; console.log(a,b); var es6 = (function () { return a + b; }); return es6; })));
可以看到箭頭函式被轉換為了function,說明babel外掛正常工作。
json外掛
為什麼要使用json外掛?
在src目錄下建立json.js檔案:
import json from "../package.json"; console.log(json.author);
內容很簡單,就是引入package.json,然後去列印author欄位。
修改rollup.config.js配置檔案:
import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import babel from "@rollup/plugin-babel"; export default { input: ["./src/json.js"],output: { file: "./dist/jsonBundle.js",};
執行打包,發現會發生如下報錯:
提示我們缺少@rollup/plugin-json外掛來支援json檔案。
json外掛的使用
來安裝該外掛:
npm i -D @rollup/plugin-json
同樣修改下配置檔案,將外掛加入plugins陣列即可。
然後再次打包,發現打包成功了,我們開啟生成的dist/jsonBundle目錄:
(function (factory) { typeof define === 'function' && define.amd ? define(factory) : factory(); }((function () { 'use strict'; var name = "rollup-experience"; var version = "1.0.0"; var description = ""; var main = "index.js"; var directories = { example: "example" }; var scripts = { build: "rollup -c -w",test: "echo \"Error: no test specified\" && exit 1" }; var author = "Cosen"; var license = "ISC"; var dependencies = { "@babel/core": "^7.11.6","@babel/preset-env": "^7.11.5","the-answer": "^1.0.0" }; var devDependencies = { "@rollup/plugin-babel": "^5.2.0","@rollup/plugin-commonjs": "^15.0.0","@rollup/plugin-json": "^4.1.0","@rollup/plugin-node-resolve": "^9.0.0" }; var json = { name: name,version: version,description: description,main: main,directories: directories,scripts: scripts,author: author,license: license,dependencies: dependencies,devDependencies: devDependencies }; console.log(json.author); })));
完美!!
tree-shaking機制
這裡我們以最開始的src/index.js為例進行說明:
import answer from "the-answer"; export default function () { console.log("the answer is " + answer); }
修改上述檔案:
const a = 1; const b = 2; export default function () { console.log(a + b); }
執行打包。開啟dist/bundle.js檔案:
(function (global,(function () { 'use strict'; var a = 1; var b = 2; function index () { console.log(a + b); } return index; })));
再次修改src/index.js檔案:
const a = 1; const b = 2; export default function () { console.log(a); }
再次執行打包,開啟打包檔案:
(function (global,(function () { 'use strict'; var a = 1; function index () { console.log(a); } return index; })));
發現了什麼?
我們發現關於變數b的定義沒有了,因為原始碼中並沒有用到這個變數。這就是ES模組著名的tree-shaking機制,它動態地清除沒有被使用過的程式碼,使得程式碼更加精簡,從而可以使得我們的類庫獲得更快的載入速度。
總結
本文大致向大家介紹了什麼是rollup以及如何快速上手rollup。文中提到的這些其實只是冰山一角,rollup能玩的東西還有很多,關於更多可以去rollup 官網查詢
到此這篇帶你從零快速上手Rollup的文章就介紹到這了,更多相關從零快速上手Rollup內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!