天天看點

一文帶你快速上手 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

對于代碼分割和靜态資源導入有着“先天優勢”,并且支援熱子產品替換(

HDR

),而

rollup

并不支援。

是以當開發應用事可以優先選擇

webpack

,但是

rollup

對于代碼的

Tree-shaking

和 ES6 子產品有着算法優勢上的支援,若你項目隻需要打包出一個簡單的

bundle

包,并是基于 ES6 子產品開發的,可以考慮使用

rollup

其實

webpack

2.0

開始就已經支援

Tree-shaking

,并在使用

babel-loader

的情況下還可以支援

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

中寫入如下代碼

然後在指令行執行以下指令

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

如我們所料的,控制台輸出了

柯森

到這裡,我們就用

rollup

打包樂意個最最簡單的

demo

可能很多同學看到這裡對于上面指令行中的參數不是很明白,我依次說明下:

  • -f

    -f

    參數是

    --format

    的縮寫,它表示生成代碼的格式,

    amd

    表示采用

    AMD

    标準,

    cjs

    CommonJS

    标準,

    esm

    (或 es )為 ES 子產品标準。

    -f

    的值可以為

    amd

    cjs

    system

    esm

    (‘

    es

    ’也可以)、

    life

    umd

    中的任何一個。
  • -o

    -o

    指定了輸出的路徑,這裡我們将打包後的檔案輸出到

    dist

    目錄下的

    bundle.js

  • -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

    表示輸出檔案的内容,它允許傳入一個對象或一個數組,當為數組時,一次輸出多個檔案,它包含一下内容:

    1.

    output.file

    : 輸出檔案的路徑(老版本為 dest,已經廢棄)

    2.

    output.format

    :輸出檔案的格式

    3.

    output.banner

    :檔案頭部添加的内容

    4.

    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"

會得到以下報錯:

一文帶你快速上手 Rollup

打包後的

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

,可以發現報錯已經沒有了:

一文帶你快速上手 Rollup

打開

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 (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-common.js

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"],
};
           

然後打包,發現會出現報錯

一文帶你快速上手 Rollup

提示我們缺少

@babel/core

,因為

@babel/core

babel

的核心。我們來進行安裝

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

提示我們缺少

@rollup/plugin-json

插件來支援

json

檔案。

json 插件的使用

來安裝該插件:

npm i -D @rollup/plugin-json
           

同樣修改下配置檔案,将插件加入

plugins

數組即可。

然後再次打包,發現打包成功了,我們打開生成的

dist/jsonBundle.js

目錄

(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

官網查詢。

繼續閱讀