一文帶你快速上手Rollup

前言

項目中一直用的都是webpack,前一段須要開發幾個類庫供其餘平臺使用,原本打算繼續用webpack的,但感受webpack用來開發js庫,不只繁瑣並且打包後的文件體積也比較大。正好以前看vue源碼,知道vue也是經過rollup打包的。此次又是開發類庫的,因而就快速上手了rolluphtml

本篇文章是我有了必定的項目實踐後,回過來給你們分享一下如何從零快速上手rollup前端

什麼是rollup

系統的瞭解rollup以前,咱們先來簡單瞭解下What is rollup?vue

關於rollup的介紹,官方文檔已經寫的很清楚了:node

Rollup 是一個 JavaScript 模塊打包器,能夠將小塊代碼編譯成大塊複雜的代碼,例如 library 或應用程序。webpack

Webpack偏向於應用打包的定位不一樣,rollup.js更專一於Javascript類庫打包。es6

咱們熟知的VueReact等諸多知名框架或類庫都是經過rollup.js進行打包的。web

爲何是rollup

webpack我相信作前端的同窗你們都用過,那麼爲何有些場景還要使用rollup呢?這裏我簡單對webpackrollup作一個比較:面試

整體來講webpackrollup在不一樣場景下,都能發揮自身優點做用。webpack對於代碼分割和靜態資源導入有着「先天優點」,而且支持熱模塊替換(HMR),而rollup並不支持。算法

因此當開發應用時能夠優先選擇webpack,可是rollup對於代碼的Tree-shakingES6模塊有着算法優點上的支持,若你項目只須要打包出一個簡單的bundle包,並是基於ES6模塊開發的,能夠考慮使用rollupnpm

其實webpack2.0開始就已經支持Tree-shaking,並在使用babel-loader的狀況下還能夠支持es6 module的打包。實際上,rollup已經在漸漸地失去了當初的優點了。可是它並無被拋棄,反而因其簡單的API、使用方式被許多庫開發者青睞,如ReactVue等,都是使用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標準, cjsCommonJS標準, esm(或 es)爲 ES模塊標準。 -f的值能夠爲 amdcjssystemesm('es’也能夠)、 iifeumd中的任何一個。
  • -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下載遠程的庫。

webpackbrowserify這樣的其餘捆綁包不一樣,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.jsonscripts中:"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"],
  output: {
    file"./dist/bundle.js",
    format"umd",
    name"experience",
  },
  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"],
  output: {
    file"./dist/bundle.js",
    format"umd",
    name"experience",
  },
  plugins: [resolve()],
  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 (ereturn 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"],
  output: {
    file: "./dist/bundle.js",
    format: "umd",
    name: "experience",
  },
  plugins: [resolve(), commonjs()],
  external: ["the-answer"],
};

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",
    format"umd",
    name"experience",
  },
  plugins: [resolve(), commonjs()],
  external: ["the-answer"],
};

執行打包,能夠看到dist/esBundle.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';

  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"],
  output: {
    file"./dist/esBundle.js",
    format"umd",
    name"experience",
  },
  plugins: [resolve(), commonjs(), babel()],
  external: ["the-answer"],
};

而後打包,發現會出現報錯:提示咱們缺乏@babel/core,由於@babel/corebabel的核心。咱們來進行安裝:

npm i @babel/core

再次執行打包,發現此次沒有報錯了,可是咱們嘗試打開dist/esBundle.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';

  const a = 1;
  const b = 2;
  console.log(a, 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, 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 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",
    format"umd",
    name"experience",
  },
  plugins: [resolve(), commonjs(), babel()],
  external: ["the-answer"],
};

執行打包,發現會發生以下報錯:提示咱們缺乏@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, 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 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, 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 a = 1;
  function index ({
    console.log(a);
  }

  return index;

})));

發現了什麼?

咱們發現關於變量b的定義沒有了,由於源碼中並無用到這個變量。這就是ES模塊著名的tree-shaking機制,它動態地清除沒有被使用過的代碼,使得代碼更加精簡,從而可使得咱們的類庫得到更快的加載速度。

總結

本文大體向你們介紹了什麼是rollup以及如何快速上手rollup。文中提到的這些其實只是冰山一角,rollup能玩的東西還有不少,關於更多能夠去rollup 官網查詢

關注我

我是山月,正致力於天天用五分鐘可以看完的簡短答案回答一個大廠高頻面試題。掃碼添加個人微信,備註進羣,加入高級前端進階羣.

歡迎關注公衆號【互聯網大廠招聘】,定時推送大廠內推信息及面試題簡答,天天學習五分鐘,半年進入大廠中


本文分享自微信公衆號 - 全棧成長之路(shanyue-road)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索