# 轻量化Library打包工具

# 简介

Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。

Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而你的项目不必携带其他未使用的代码。ES6 模块最终还是要由浏览器原生实现,但当前 Rollup 可以使你提前体验。

# Why is rollup

Rollup 着眼于未来,采用原生的ES 标准的模块机制进行构建,未来ES 规范肯定会由浏览器实现,也是JavaScript语言明确的发展方向。机制更加优于CommonJS

CommonJS 是在ES规范之前被提出的一种暂时性性解决方案,是一种特殊的传统格式,

相比较ES模块允许进行静态分析,从而实现像 tree-shaking 的优化,并提供诸如循环引用和动态绑定等高级功能

Tree-shaking, 也被称为 "live code inclusion," 它是清除实际上并没有在给定项目中使用的代码的过程,但是它可以更加高效。

# 构建工具对比

打包工具 体积 注入代码 code spliting dynamic import Tree-shaking
webpack large more
rollup small less

webpack 是一款强大的 bundle 构建工具,通过 loader机制可以处理各种类型的文件,良好的 code splittingdynamic import支持使得webpack 成为了应用程序,单页应用的全能型的的打包工具。

但是因为其打包体积相对较大,注入代码更多,没有良好的Tree-shaking所以在Library 的打包工作中,不如rollup打包的精简。

# 配置

rollup 定位是一款轻量级的构建工具,其配置也相对很简单,但是由于不支持CommonJs,在配置rollup-plugin-commonjsCommmonJS 转化成ES 模块方式的时候,需要配置babel 转译

由于babel 7+升级改动比较大,命名空间改动,市面上的教程大部分说的不够清楚,而且基本上停留在babel 6的配置,导致一直抛(plugin commonjs) SyntaxError: Unexpected token

在后文中会着力介绍这个部分,分析babel 7+配置以及各个presetsplugins 的作用。

# 安装rollup

npm install --save-dev rollup
1

# 配置文件

在项目根目录创建rollup.config.js文件。

rollup 配置比较的简洁,一般包括input,output,plugins,external

配置项参考

这是一段简单的配置文件

export default {
  input: 'src/index.js',
  output: {
    name: 'amapUpper',
    file: 'dist/amapUpper.js',
    format: 'umd' //兼容模式
  },
  external: ['@amap/amap-jsapi-loader'],// 配置引入的包是否要打包,在这里配置的会忽略掉,不打包到我们的程序里面
  plugins: []
};
1
2
3
4
5
6
7
8
9
10

当你的配置文件需要配置多个打包策略的时候,你还可以这样配置

export default [{
  input: 'main-a.js',
  output: {
    file: 'dist/bundle-a.js',
    format: 'cjs'
  }
}, {
  input: 'main-b.js',
  output: [
    {
      file: 'dist/bundle-b1.js',
      format: 'cjs'
    },
    {
      file: 'dist/bundle-b2.js',
      format: 'es'
    }
  ]
}]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

你还可以使用多个配置文件,使用--config来使用配置文件

# pass a custom config file location to Rollup
rollup --config my.config.js
1
2

WARNING

使用了umd模式必须指定Name for UMD export

# 配置rollup插件

配置gollup 的plugin 类似babel 全部移植到了@rollup下,在官方提供的 一站式配置商店,可以下载所需的插件, 作用在于可维护学习被误导成本降低,更有利于长期维护。

@plugin 链接地址

# 插件是什么

rollup plugin 是一个遵循rollup插件规范的object,一般通过一个工厂函数返回一个对象实现

一段简单的示例:

export default function myExample () {
  return {
    name: 'my-example', // this name will show up in warnings and errors
    resolveId ( source ) {
      if (source === 'virtual-module') {
        return source; // this signals that rollup should not ask other plugins or check the file system to find this id
      }
      return null; // other ids should be handled as usually
    },
    load ( id ) {
      if (id === 'virtual-module') {
        return 'export default "This is virtual!"'; // the source code for "virtual-module"
      }
      return null; // other ids should be handled as usually
    }
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

然后在配置文件中引用该插件,在plugins中,传入执行返回的对象

import myExample from 'myExample'


plugins: [ myExample() ]
1
2
3
4

# 插件对象遵循的规范

A Rollup plugin is an object with one or more of the properties, build hooks, and output generation hooks

# 插件执行顺序

熟悉webpack loader机制的都应该知道loader 实际上是从右到左,自下而上执行的,在页头的 rollup打包js的注意点 里面提到的错误记录/错误2里面,类比webpack loader机制, 其实是一个错误的类比,实际上rollupplugin 机制是从左往右,自上而下而下的执行顺序。

一个测试例子

// rollup.config.js
function myExample1() {
  return {
    name: 'my-example1', // this name will show up in warnings and errors
    resolveId(source) {
      console.log('111 resolve_______________-------------------------------------------');
      return null; // other ids should be handled as usually
    },
    load(id) {
      console.log('111 load_______________-------------------------------------------');
      return null; // other ids should be handled as usually
    }
  };
}
function myExample2() {
  return {
    name: 'my-example1', // this name will show up in warnings and errors
    resolveId(source) {
      console.log('222 resolve_______________-------------------------------------------');
      return null; // other ids should be handled as usually
    },
    load(id) {
      console.log('222 load_______________-------------------------------------------');
      return null; // other ids should be handled as usually
    }
  };
}


export default {
  input: 'src/index.js',
  output: {
    name: 'test',
    file: 'dist/test.js',
    format: 'umd'
  },
  plugins: [
    myExample1(),    
    myExample2()
  ]
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

可以看到load,resolve 钩子函数都是从左往右,自上而下而下的执行顺序。

# 配置常用的插件

下面介绍正常打包中需要用到的插件,和一般配置。

  • @rollup/plugin-jsonrollup-plugin-json
  • @rollup/plugin-commonjsrollup-plugin-commonjs
  • @rollup/plugin-node-resolverollup-plugin-node-resolve
  • rollup-plugin-terser

首先,需要作为开发依赖安装这些插件

npm i @rollup/plugin-json -D
npm i @rollup/plugin-commonjs -D
npm i @rollup/plugin-node-resolve -D
npm i rollup-plugin-terser -D
1
2
3
4

然后需要引用配置文件

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

export default {
  ...
  plugins: [
    json(),
    terser(),
    nodeResolve(),
    commonjs(),
    babel({
      exclude: '*', // 排除node_modules 下的文件
      runtimeHelpers: true // 防止生成多个 打包helper 函数
    }),
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 问题总结

# commonjs 插件和 babel7+ 配置

在跟着 rollup 搭建打包 JS 一文配置的过程中,用到的插件不是官方一站式插件提供的长期维护版本,遇到了 rollup-plugin-commonjs 插件打包报错的问题

[!] (plugin commonjs) SyntaxError: Unexpected token (25:8)

针对使用了放弃维护的rollup-plugin-commonjs插件的打包抛出的这个SyntaxError,可以有两种解决方案。推荐方案二。

# 解决方案一 配置babel

查找了很多资料都显示,应该是babel 配置问题,主要分为:

  • babel 配置问题
  • babel 转译执行顺序问题

综合这些资料,这个报错应该是由于该commonjs 插件库 被移动到 @rollup/plugin-commonjs下维护,导致非长期维护版本缺少某些转译后的辅助函数,属性,导致抛出异常。

所以需要babel 辅助编译之后才能正确执行commonjs 插件。

babel 配置的一些问题

由于babel 7+版本中,将babel 的各个库全部移植到了@babel/***下统一官方维护,而市面上大部分的babel教程实际上没有对这点进行详细说明,而且教程里安装babel组件的时候并没有指定版本,实际上导致可能安装了7+ 版本的babel核心库,而安装了放弃维护的babel-plugins-***下的插件预设之类的。就会导致一系列的问题。

这里要注意一下这个@这个符号,这个是只有babel7才特有的,babel6都木有,市面上大量代码都是基于6的所以要特别注意,安装的时候要使用 npm install i -S @babel\cli 而不能是npm install i -S babel-cli了 这是 babel 7 的一大调整,原来的 babel-xx 包统一迁移到babel域 下 - 域由 @ 符号来标识

规划

接下来会顺着这篇文章出一篇总结babel 7配置问题的 文章,加油吧!奥利给!!!

目前可以参阅文章开头提到的 babel 7 的使用的个人理解

配置

由于这里使用@rollup/plugin-babel,执行babel 转译 所以不需要安装@babel/cli命令行工具。

首先我们需要安装:

  • babel核心包@babel/core
  • babel预设包@babel/preset-env
  • 统一的模块化的helper@babel/runtime
  • helper 的自动化引入工具 @babel/plugin-transform-runtime
npm i -D @babel/core
npm i -D @babel/preset-env
npm i -D @babel/runtime
npm i -D @babel/plugin-transform-runtime
1
2
3
4

然后在项目根目录下创建.babelrc的配置文件

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "targets": {
          "browsers": [
            "> 1%",
            "last 2 versions",
            "not ie <= 8"
          ]
        }
      }
    ]
  ],
  "plugins": ["@babel/plugin-transform-runtime"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

然后如果你用的是rollup-plugin-commonjs非常期维护版本的插件的话,由于和babel有依赖关系,你需要:

commonjs插件之前引入babel插件,并配置需要匹配编译的文件,目录。和编译配置

// rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    name: 'amapUpper',
    file: 'dist/amapUpper.js',
    format: 'umd'
  },
  plugins: [
    babel({
      exclude: '/node_modules/**', // 排除node_modules 下的文件
      runtimeHelpers: true // 与plugin-transform-runtime 插件对应配置,生成统一化helper。
    }),
    commonjs()
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 解决方案二

使用rollup新的官方提供的移动后的一站式插件库里面的插件

rollup @plugin 链接地址

使用了新的插件库之后,babel还是需要配置的,只是解决了commonjs 插件依赖babel的问题。

而babel 实际上是解决各种浏览器,引擎之间的差异,而存在的。所以为了更好的支持,需要配备良好的babel配置。