手把手帶你走進下一代的ES6模塊打包工具—Rollup

本文一共七個例子,由淺入深帶你熟悉Rollup。首先把 rollup-demos 這個示例倉庫下載到本地node

mkdir rollup
cd rollup
git clone git@github.com:qiqihaobenben/rollup-demos.git

準備就緒,正文開始react


簡介

如下內容基於Webpack和Rollup這兩個打包工具來展開。 webpack

工具的使用是分場景的,Rollup的使用場景是,你的代碼基於 ES6 模塊編寫,而且你作的東西是準備給他人使用的。 git

有一句經驗之談:在開發應用時使用 Webpack,開發庫時使用 Rollup。github

例如:React、Vue、Ember、Preact、D三、Three.js、Moment 等衆多知名項目都使用了 Rollupweb

優勢
  • 編譯運行出來的代碼內容格式可讀性好。
  • 幾乎沒什麼多餘代碼,除了必要的cjs, umd頭外,bundle代碼基本和源碼沒什麼差別,沒有奇怪的__webpack_require__, Object.defineProperty
  • 相比Webpack,Rollup擁有無可比擬的性能優點,這是由依賴處理方式決定的,編譯時依賴處理(Rollup)天然比運行時依賴處理(Webpack)性能更好,並且沒什麼多餘代碼,如上文提到的,webpack bundle不只體積大,非業務代碼(__webpack_require__, Object.defineProperty)執行耗時也不容小視。Rollup沒有生成這些額外的東西,執行耗時主要在於Compile ScriptEvaluate Script 上,其他部分能夠忽略不計
  • 支持ES6模塊和IIFE格式。
  • 對於ES6模塊依賴庫,Rollup會靜態分析代碼中的 import,並將排除任何未實際使用的代碼。(Tree-shaking)
缺點
  • 插件生態相對較弱,一些常見需求沒法知足

好比打包多個依賴庫,把公共依賴項提出來(Webpack的CommonsChunkPlugin)還有HMR(模塊熱替換)npm

  • 文檔相對較少,遇到問題沒法快速解決

安裝

npm install -g rollupjson

所有指令

Usage: rollup [options] <entry file>

Basic options:

-v, --version               Show version number
-h, --help                  Show this help message
-c, --config                Use this config file (if argument is used but value
                              is unspecified, defaults to rollup.config.js)
-w, --watch                 Watch files in bundle and rebuild on changes
-i, --input                 Input (alternative to <entry file>)
-o, --output.file <output>  Output (if absent, prints to stdout)
-f, --output.format [es]    Type of output (amd, cjs, es, iife, umd)
-e, --external              Comma-separate list of module IDs to exclude
-g, --globals               Comma-separate list of `module ID:Global` pairs
                              Any module IDs defined here are added to external
-n, --name                  Name for UMD export
-m, --sourcemap             Generate sourcemap (`-m inline` for inline map)
-l, --legacy                Support IE8
--amd.id                    ID for AMD module (default is anonymous)
--amd.define                Function to use in place of `define`
--no-strict                 Don't emit a `"use strict";` in the generated modules.
--no-indent                 Don't indent result
--environment <values>      Settings passed to config file (see example)
--no-conflict               Generate a noConflict method for UMD globals
--no-treeshake              Disable tree-shaking
--silent                    Don't print warnings
--intro                     Content to insert at top of bundle (inside wrapper)
--outro                     Content to insert at end of bundle (inside wrapper)
--banner                    Content to insert at top of bundle (outside wrapper)
--footer                    Content to insert at end of bundle (outside wrapper)
--interop                   Include interop block (true by default)

配置文件細則

export default {
  // 核心選項
  input,     // 必須
  external,
  plugins,

  // 額外選項
  onwarn,

  // danger zone
  acorn,
  context,
  moduleContext,
  legacy

  output: {  // 必須 (若是要輸出多個,能夠是一個數組)
    // 核心選項
    file,    // 必須
    format,  // 必須
    name,
    globals,

    // 額外選項
    paths,
    banner,
    footer,
    intro,
    outro,
    sourcemap,
    sourcemapFile,
    interop,

    // 高危選項
    exports,
    amd,
    indent
    strict
  },
};

簡單實例

生成瀏覽器可用
//打包main.js到bundle.js 打包格式是當即執行函數
rollup main.js -o bundle.js -f iife
生成Node.js可用
//打包main.js到bundle.js 打包格式是commonjs。
rollup main.js -o bundle.js -f cjs
Node.js和瀏覽器均可用
//打包main.js到bundle.js 打包格式是UMD,這個格式須要一個模塊名
rollup main.js -o bundle.js -f umd --name "myBundle"
運行配置文件

rollup -csegmentfault

實際操做

example1

// src/example1/main.js
import one from './module1.js';
export default function () {
    console.log(one);
}

// src/example1/module1.js
export default 'hello world!'

項目根目錄(以後Rollup運行會默認這個目錄)運行
rollup src/example1/main.js -o dist/example1/bundle.js -f cjs 數組

解析:
-f 選項( --output.format 的縮寫)指定了所建立 bundle 的類型,打包時必需要有的選項,不然會報錯。
輸出的格式有amd, cjs, es, iife, umd,能夠把命令行中 -f 後面的 cjs 改成其餘的,看一下生成的bundle.js的內容有什麼不同。對於模塊不熟悉的能夠看一下 很全很全的JavaScript的模塊講解

-o--output.file 的縮寫,若是不寫會默認輸出到命令行終端(標準輸出)。

example2

若是添加更多的選項,上面這種命令行的方式就顯得麻煩了,就得須要 使用配置文件 了。

在項目 src/example2 文件夾下,新建一個 rollup.config.js 文件,寫入如下代碼:

export default {
    input: 'src/example2/main.js',
    output: {
        file: 'dist/example2/bundle.js',
        format: 'cjs'
    }
}

新建一個main.jsmodule2.js以下:

// src/example2/main.js
import one from './module2.js';
export default function () {
    console.log(one);
}

// src/example1/module2.js
export default 'hello config!'

接下來就是運行命令,rollup.config.js原本是Rollup默認運行的配置文件,若是咱們的rollup.config.js是放在根目錄下的,能夠直接運行rollup -c,不用任何選項,可是咱們是放在src/module2文件夾下的,因此要加上配置文件的路徑
rollup -c src/module2/rollup.config.js

注意

  1. 一樣的命令行選項將會覆蓋配置文件中的選項,例如:

rollup -c src/module2/rollup.config.js -o dist/example2/bundle2.js 那麼打包好的文件名就是bundle2.js

  1. Rollup 自己會處理配置文件,因此可使用 export default 語法——代碼不會通過 Babel 等相似工具編譯,因此只能使用支持 ES2015(ES6) 語法的 Node.js 版本。

example3

隨着構建更復雜的 bundle,咱們須要加入插件(plugins)。

使用 rollup-plugin-json,令 Rollup 從 JSON 文件中讀取數據。
將 rollup-plugin-json 安裝爲開發依賴,由於代碼實際執行時不依賴這個插件——只是在打包時使用,因此用的是--save-dev 而不是 --save

npm i -D rollup-plugin-json 或者 npm install --save-dev rollup-plugin-json

src/example3文件夾下新建 main.jsrollup.config.js

// main.js
import { version} from '../../package.json';

export default function () {
    console.log(`version is ${version}`);
}

// rollup.config.js
import json from 'rollup-plugin-json';

export default {
    input: 'src/example3/main.js',
    output: {
        file: 'dist/example3/bundle.js',
        format: 'cjs'
    },
    plugins: [
        json()
    ]
}

運行命令 rollup -c src/example3/rollup.config.js

擴展: json函數能夠傳入 include指定包含文件、exclude指定排除文件,preferConst若是爲true,用const接受輸出,若是爲false,用 var接收輸出。

注意: tree-shaking的做用,能夠看到打包好bundle.js中只有version輸入,package.json 中的其它數據被忽略了。

example4

Rollup 不知道怎麼處理依賴於從 npm 安裝到你的 node_modules 文件夾中的軟件包。

例如,添加一個簡單的依賴 the-answer,它輸出對生活、宇宙及其它一切的答案,這個簡單的包是用來演示如何將npm包彙總到Rollup包中。特別是, 此包在package.json中添加了 "main" (UMD 格式) 和 "模塊" (ES2015 格式)這個兩個選項。

看一下,按照普通流程引入 the-answer 模塊會是什麼結果。
npm install the-answer
src/example4 文件夾下新增 main.jsrollup.config.js

// main.js
import answer from 'the-answer';

export default function () {
    console.log('the answer is ' + answer);
}


// rollup.config.js
export default {
    input: 'src/example4/main.js',
    output: {
        file: 'dist/example4/bundle.js',
        format: 'cjs'
    },
    plugins: [
        // 沒有加入任何插件
    ]
}

運行: rollup -c src/example4/rollup.config.js 會有一個警告 Unresolved dependencies ,咱們看一下 打包好的dist/example4/bundle.js

// 截取dist/example4/bundle.js`
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var answer = _interopDefault(require('the-answer'));

// 能夠看到the-answer並無打包進來,還得用node的require去請求,而後通過函數轉化才能拿到the-answer的輸出值
// 咱們能夠看一下 node_modules 下的 the-answer 模塊暴露出的內容

var index = 42;
export default index;

// 這樣也能夠看出,若是the-answer若是打包進來,應該是:
var answer = 42;

如今咱們須要一個插件 rollup-plugin-node-resolve 來告訴 Rollup 如何查找外部模塊

npm i -D rollup-plugin-node-resolve

將插件加入配置文件中

import resolve from 'rollup-plugin-node-resolve';

export default {
    input: 'src/example4/main.js',
    output: {
        file: 'dist/example4/bundle.js',
        format: 'cjs'
    },
    plugins: [
        resolve()
    ]
}

再次運行rollup -c src/example4/rollup.config.js 沒有警告 ,咱們看一下打包好的dist/example4/bundle.js

'use strict';

// the-answer的輸出已經打包進來了
var index = 42;

function main () {
    console.log('the answer is ' + index);
}

module.exports = main;

example5

相似 the-answer 一些庫由於 package.json裏的module選項可讓咱們正常導入的ES6模塊。 可是目前,npm中的大多數包都是以CommonJS模塊的形式出現的。 在它們更改以前,咱們須要將CommonJS模塊轉換爲 ES2015 供 Rollup 處理。

rollup-plugin-commonjs 插件就是用來將 CommonJS 轉換成 ES2015 模塊的。一般,這個插件會跟 rollup-plugin-node-resolve配合使用,這樣就能打包 node_modules依賴中的CommonJS。
rollup-plugin-commonjs 應該用在其餘插件轉換你的模塊以前 - 這是爲了防止其餘插件的改變破壞CommonJS的檢測。

安裝:npm i -D rollup-plugin-commonjs

src/example5文件夾下新建 main.jsmodule5.js rollup.config.js, 用來驗證插件。

// module5.js
exports.named = 'cfangxu';
//module.exports = {named: 'cfangxu'} 這個會報錯,可是插件文檔裏說是好的,給他提一個issues

// main.js
import { named } from './module5.js';
export default function () {
   console.log(named);
}

// rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';

export default {
   input: 'src/example5/main.js',
   output: {
       file: 'dist/example5/bundle.js',
       format: 'cjs'
   },
   plugins: [
       resolve({
           jsnext: true,
           main: true
       }),
       commonjs()
   ]
}

注意: 若是引入的是 node_modules裏的模塊
例如:import { named } from 'my-lib';
要啓用 namedExports 選項顯示的指定命名輸出。固然你也能夠總體都引入
即: import all from 'my-lib';

example6

external 接受一個模塊名稱的數組或一個接受模塊名稱的函數(若是它被視爲外部引用(externals)則返回true)

安裝 lodashnpm i -S lodash

src/example6 文件夾中新建 main.jsrollup.config.js

// main.js
import answer from 'the-answer';
import _ from 'lodash';

// rollup.config.js
import resolve from 'rollup-plugin-node-resolve';

export default {
    input: 'src/example6/main.js',
    output: {
        file: 'dist/example6/bundle.js',
        format: 'umd',
        name: 'example6'
    },
    plugins: [
        resolve()
    ],
    external: ['lodash']
}

配置文件中加入 external 就不會把第三方的庫打包進咱們最後的文件了。能夠在 src/example5/rollup.config.js 中把 external 註釋掉看看打包後的文件,會把整個 lodsh 打包進來。
擴展: 若是用到 lodsh ,可使用 babel-plugin-lodash 來最優選擇lodash模塊。

example7

咱們在項目中有很大機率用到 babel ,使用 Babel 和 Rollup 的最簡單方法是使用 rollup-plugin-babel

安裝: npm i -D rollup-plugin-babel

src/example7文件夾下新建 main.js .babelrc rollup.config.js

//main.js
import answer from 'the-answer';

export default function () {
    console.log(`the answer is ${answer}`);
}

//.babelrc
{
    "presets": [
        ["env",{
            "modules": false
        }]
    ],
    "plugins": [
        "external-helpers"
    ]
}

//rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';

export default {
    input: 'src/example7/main.js',
    output: {
        file: 'dist/example7/bundle.js',
        format: 'cjs'
    },
    plugins: [
        resolve(),
        babel({
            exclude: 'node_modules/**',
            externalHelpers: true
        })
    ]
}

安裝: npm i -D babel-core babel-preset-env babel-plugin-external-helpers

運行:rollup -c src/example7/rollup.config.js

// dist/example7/bundle.js
'use strict';

var index = 42;

function main () {
    // 轉成了ES5的語法了
    console.log('the answer is ' + index);
}

module.exports = main;

說明

  • babel-plugin-external-helpers 這個模塊是在 .babelrc 文件中體現,目的是讓babel轉義出來的幫助性代碼只在該文件的頭部出現一次,而不會再每一個引入的模塊中加入,若是不想把這些幫助性的代碼打包進你的文件,須要在rollup的配置文件中加入 externalHelpers: true,這樣就會引用一個全局的babelHelpers 對象

推薦資料

相關文章
相關標籤/搜索