iT邦幫忙

2

Rollup - 基礎介紹

Ares 2021-07-10 21:42:585764 瀏覽

Rollup 是一個 JavaScript 的打包工具,目前我們常使用的 Webpack 也常拿來跟他做比較,基本上兩者皆能達到我們想要的效果,但其各自有較擅長的領域,Webpack 適合應用程式的打包,而 Rollup 則比較適合 library 的打包,另外他使用上也比 Webpack 簡單一些,接著就看一下怎麼使用吧!

建立專案

首先我們開一個新專案,使用指令建立 package.json

$ npm init -y

然後我們簡單的寫一些東西

// index.js

const myFunction = async () => {
  console.log('myFunction')
}

myFunction()

export default myFunction

運行後可以看到結果,如下:

$ node index.js
myFunction

ES6 Module

Rollup 是一個針對 ES6 Module 所設計的打包工具,所以我們必須使用 ES6 Module 來做撰寫,而使用方法有以下兩種,選一種即可開始使用囉

  1. ES Module 檔案的副檔名改為 .mjs
  2. package.json 內加入 { "type": "module" }

開始打包

首先安裝今天的主角 Rollup

$ npm install --global rollup

接著可以執行指令進行打包

$ rollup index.js --file dist/bundle.js --format umd --name "myBundle"
  • -f, --format:檔案輸出格式(amd, cjs, es, iife, umd, system)
  • -n, --name:檔案全域名稱
  • -m, --sourcemap:產生 sourcemap
  • -w, --watch:監聽檔案變化即時編譯
  • -c, --config:使用 rollup.config.js 的設定

rollup.config.js

當需要的設定變多之後,我們可以創建 rollup.config.js 來詳細寫入相關的設定,以下有幾個常用設定,詳細的可以看官方文件

// rollup.config.js

const config = {
  input: 'index.js', // 進入點
  plugins: [], // 插件
  external: [], // 外部插件
  onwarn(warning, warn) { // 自定義警告
    // do something...
  },
  treeshake: true, // 刪除沒用到的程式碼
  output: { // 輸出檔案
    name: 'bundle', // 全域名稱
    file: 'dist/bundle.js', // 輸出檔案
    format: umd, // 輸出格式
    sourcemap: true // 是否產生 sourcemap
  }
}

export default config

最後我們可以在 package.json 加入指令,方便之後編譯

// package.json

{
  "scripts": {
    "build": "rollup -c"
  }
}

打包成果如下:

(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.bundle = factory());
}(this, (function () { 'use strict';

  const myFunction = async () => {
    console.log('myFunction');
  };

  myFunction();

  return myFunction;

})));

安裝插件

通常打包時我們通常還會用到許多其他的功能,而插件就是來補齊這些功能,首先我們先修改一下程式碼,安裝一個 Demo 用的插件,並小小修改一下程式碼

$ npm install the-answer

接著程式碼如下:

// index.js

import answer from 'the-answer'

const action = () => {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(answer)
    }, 3000)
  })
}

const myFunction = async () => {
  const answer = await action()
  console.log(answer)
}

myFunction()

export default myFunction

打包後會發現有一些錯誤,這些錯誤會靠著插件來解決,所以我們先看看有哪些常見的插件可以使用

@rollup/plugin-node-resolve

resolve 協助我們從 node_modules 中找到我們安裝的插件

$ npm install @rollup/plugin-node-resolve --save-dev
// rollup.config.js

import { nodeResolve } from '@rollup/plugin-node-resolve'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    nodeResolve()
    // 如果要在瀏覽器使用需要加入設定如下
    // nodeResolve({ browser: true, preferBuiltins: true })
  ]
}

export default config

@rollup/plugin-commonjs

commonjsCommonJS 轉換為 ES6 Module

$ npm install @rollup/plugin-commonjs --save-dev
// rollup.config.js

import commonjs from '@rollup/plugin-commonjs'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    commonjs()
    // Node.js 有些套件需要加入以下設定才會正常
    // commonjs({ include: ['node_modules/**'] })
  ]
}

export default config

@rollup/plugin-json

json.json 檔案轉換為 ES6 Module

$ npm install @rollup/plugin-json --save-dev
// rollup.config.js

import json from '@rollup/plugin-json'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    json()
  ]
}

export default config

rollup-plugin-node-builtins & rollup-plugin-node-globals

builtinsglobals 將一些 Node.js 內的全域變數變成 ES6 Module

$ npm install rollup-plugin-node-builtins --save-dev
$ npm install rollup-plugin-node-globals --save-dev
// rollup.config.js

import builtins from 'rollup-plugin-node-builtins'
import globals from 'rollup-plugin-node-globals'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    globals(),
    builtins()
  ]
}

export default config

@rollup/plugin-babel

babel 將 ES6 的語法編譯為瀏覽器看得懂的版本,另外還要下載它的核心

$ npm install @rollup/plugin-babel --save-dev
$ npm install @babel/core --save-dev
// rollup.config.js

import { babel } from '@rollup/plugin-babel'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    babel({ babelHelpers: 'bundled' })
  ]
}

export default config

接著要編寫 babel 的設定檔,我們在根目錄建立一個 babel.config.js,並安裝 babel 的預設設定

$ npm install @babel/preset-env --save-dev
// babel.config.js

const config = {
  presets: [
    [
      '@babel/preset-env'
    ]
  ]
}

export default config

最後在進入點加入 runtime.js 使其正常編譯

// index.js

import 'regenerator-runtime/runtime.js'

rollup-plugin-terser

terser 用來將程式碼壓縮

$ npm install rollup-plugin-terser --save-dev
// rollup.config.js

import { terser } from 'rollup-plugin-terser'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    terser()
  ]
}

export default config

以上差不多就是常用的插件,那麼我們來把剛剛編譯的問題解決吧!

瀏覽器版本設定

// rollup.config.js

import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import { babel } from '@rollup/plugin-babel'
import { terser } from 'rollup-plugin-terser'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    nodeResolve({ browser: true, preferBuiltins: true }),
    commonjs(),
    babel({ babelHelpers: 'bundled' }),
    terser()
  ]
}

export default config

Node.js 版本設定

// rollup.config.js

import { nodeResolve } from '@rollup/plugin-node-resolve'
import builtins from 'rollup-plugin-node-builtins'
import globals from 'rollup-plugin-node-globals'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import { babel } from '@rollup/plugin-babel'
import { terser } from 'rollup-plugin-terser'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    nodeResolve(),
    globals(),
    builtins(),
    commonjs({ include: ['node_modules/**'] }),
    json(),
    babel({ babelHelpers: 'bundled' }),
    terser()
  ]
}

export default config

到這邊就可以正常打包囉!

外部插件

順便補充一下,如果想要把插件當成外部插件引用可以照以下設定,以瀏覽器舉例

// rollup.config.js

import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import { babel } from '@rollup/plugin-babel'

const config = {
  input: 'index.js',
  output: {
    name: 'bundle',
    file: 'dist/bundle.js',
    format: 'umd',
    globals: {
      'the-answer': 'the-answer' // 全域變數
    }
  },
  plugins: [
    nodeResolve({ browser: true, preferBuiltins: true }),
    commonjs(),
    babel({ babelHelpers: 'bundled' })
  ],
  external: ['the-answer'] // 告知為外部插件
}

export default config

最後另外提醒一下以下幾點

  • Node.js 與瀏覽器環境有差異,要分開調整
  • 插件的載入順序會有差異,要特別注意
  • 不使用 ES6 Module 撰寫的話打包路徑有可能會出現錯誤

結語

我只是想打包個 JavaScript 阿,搞這一大堆的搞了好幾天,真的很苦啊嗚嗚,希望不要有人再掉進這個大坑


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
JeffreyChen
iT邦新手 4 級 ‧ 2021-12-04 20:43:02

我也是常常想要使用第三方的npm模組,結果總是出一堆問題...
請問是從哪邊找到這些套件對應的使用情況?
rollup-plugin-node-builtins
rollup-plugin-node-globals
還是不知道什麼情況下要加入這些套件
主要原因是 rollup 跳出的 Error log 方向都好歪...

Ares iT邦研究生 3 級 ‧ 2021-12-05 10:37:43 檢舉

我自己也是不斷的 try and error,google 找答案的時間都比寫 code 時間長了,globals 跟 builtins 主要是用 node 打包時會用到這些東西

我要留言

立即登入留言