Rollup - 基础介绍

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 阿,搞这一大堆的搞了好几天,真的很苦啊呜呜,希望不要有人再掉进这个大坑


<<:  Guaranteed Results with Updated SASM PDF Questions

>>:  进击的软件工程师之路-软件战斗营 第二十周(结训周)

Day 16:把做好的 HTML 加入 Angular 吧!

三天前,我们已成功把静态档案加入 Angular 的专案了,如果感到有点陌生,可以再到以下连结,重新...

[ Day16] Esp32s用AP mode + Relay - (程序码讲解)

1.前言 今天依旧接续之前没讲完程序码,这次主要是讲loop里面的程序码,那此次主要介绍较为主要的部...

Elastic Kibana Infographic: 资讯图像化可以炫技到什麽程度 (31)

在经过一个月的学习之後,操作上只是理解有非常多可以客制化的部分,难免会想资讯图像化的部分到底可以炫技...

第47天-学习一次性排程工作 at

今天进度 : 鸟哥私房菜 - 第十五章、例行性工作排程(crontab) 使用 systemctl ...

信任与安全感

前言 今天想跟大家讨论这个在团队内外之间都可能面对的「信任」问题。信任这件事,几乎大家都能理解它的重...