Rollup模块打包踩坑指南

2023-04-22 16:40:34 浏览数 (3)

Rollup是一个轻量级javascript模块打包器。相比于Webpack,Rollup更适合打包library。Rollup基于ES6模块,ES模块允许通过静态分析,实现tree-shaking优化,删除冗余代码。

对于Rollup和Webpack的选择,可以看下Rollup的官方文档:

Rollup 是用来构建库还是应用程序?(Is Rollup meant for building libraries or applications?) Rollup 已被许多主流的 JavaScript 库使用,也可用于构建绝大多数应用程序。但是 Rollup 还不支持一些特定的高级功能,尤其是用在构建一些应用程序的时候,特别是代码拆分和运行时态的动态导入 dynamic imports at runtime. 如果你的项目中更需要这些功能,那使用 Webpack可能更符合你的需求。

本文记录了打包一个js组件,并发布到npm的过程。

这次示例我们打包一个基于mobile-select封装的城市选择组件,先来看下将要被打包的代码。

代码语言:javascript复制
import CityList from './list'
import MobileSelect from 'mobile-select'

const CitySelector = {}
CitySelector.districtData = null
CitySelector.instance = null
CitySelector.callback = null

/**
 * 初始化方法
 * @param trigger  省份城市控件id
 * @param title  标题
 * @param callback (indexArr, data) data是一个数组
 * @param type 类型 1 选择省市 2 选择省市区
 */
CitySelector.init = function (trigger, callback, title = "请选择省份城市", type = 1) {
    CitySelector.callback = callback

    CitySelector.districtData = CityList
    CitySelector.doInit(trigger, title, type)

}

// ***

export default CitySelector

可以看到,我们的代码import了两个外部库,list是存在本地的城市区划列表,mobile-select是一个开源的选择弹框组件。

1. 初始化项目

代码语言:javascript复制
npm init

会生成一个package.json文件,修改下配置。

代码语言:javascript复制
{
  "name": "tip-city-selector",
  "version": "1.0.5",
  "description": "cn city selection component",
  "main": "dist/tip-city-selector.js",
  "scripts": {
    "build": "rollup -c"
  },
  "author": "luciozhang",
  "license": "ISC",
  "devDependencies": {},
  "dependencies": {}
}

2. ESLint代码检查

通过代码规范,避免不必要的bug。

代码语言:javascript复制
npm install --save-dev eslint

3. 安装Rollup

代码语言:javascript复制
npm install --global rollup

4. 安装Babel相关插件

为了使用JavaScript的新特性。

https://www.rollupjs.com/guide/tools/#babel

关于babel的使用,推荐阅读下面这篇文章:

你真的会用 Babel 吗?

代码语言:javascript复制
npm i -D rollup-plugin-babel

rollup-plugin-babel插件并不包含babel包,需要安装必要的babel包依赖。

代码语言:javascript复制
npm i -D @babel/core @babel/preset-env

配置rollup.config.js

代码语言:javascript复制
// rollup.config.js
import babel from 'rollup-plugin-babel';

export default {
  input: 'src/main.js',  //打包入口
  output: {
    file: 'bundle.js',  //输出文件
    format: 'cjs'       //模块化方式
  },
  plugins: [
    babel({
      exclude: 'node_modules/**' // 只编译我们的源代码,忽略第三方代码
    })
  ]
};

创建babel配置.babelrc

代码语言:javascript复制
//.babelrc
{
  "presets": [
    ["latest", {
      "es2015": {
        "modules": false //设置"modules": false,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS,导致 Rollup 的一些处理失败
      }
    }]
  ],
  "plugins": ["external-helpers"] //允许 Rollup 在包的顶部只引用一次 “helpers”,而不是每个使用它们的模块中都引用一遍(这是默认行为)
}

安装插件babel-preset-latest和babel-plugin-external-helpers

代码语言:javascript复制
npm i -D babel-preset-latest babel-plugin-external-helpers

运行一下,rollup -c。

报错了!

代码语言:javascript复制
[!] (plugin babel) Error: /Users/luciozhang/tip-city-selector/src/main.js: Babel 7.0.0-beta.56 has dropped support for the 'helpersNamespace' utility.If you are using @babel/plugin-external-helpers you will need to use a newer version than the one you currently have installed. If you have your own implementation, you'll want to explore using 'helperGenerator' alongside 'file.availableHelper()'.

看了下rollup-plugin-babel的issues,原来Babel7.x版本不建议使用babel-plugin-external-helpers,也没对其进行支持。

对比了下英文文档,是中文文档翻译不及时的锅。

我们修改下.babelrc文件去掉babel-preset-latest babel-plugin-external-helpers两个插件。

代码语言:javascript复制
//.babelrc
{
  "presets": [
    ["@babel/env", {"modules": false}]
  ]
}

5. Node 模块位置解析

再次运行rollup -c。

可以了!!

但发现一个WARN

代码语言:javascript复制
(!) Missing global variable name
Use output.globals to specify browser global variable names corresponding to external modules
mobile-select (guessing 'MobileSelect')

我们已经安装了mobile-select模块,但没有被找到。

我们需要rollup-plugin-node-resolve 插件,来告知Rollup怎么查找外部模块,安装它

代码语言:javascript复制
npm install -D rollup-plugin-node-resolve
代码语言:javascript复制
//rollup.config.js
 plugins: [
       // …
    resolve({
       browser: true,
    }),
    // …
 ]

6. 加载 CommonJS 模块

再再次运行rollup -c。

WARN没了。但出现了一个Error。

代码语言:javascript复制
[!] Error: 'default' is not exported by node_modules/mobile-select/mobile-select.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module

mobile-select模块是CommonJS模块,在Rollup中,加载CommonJS模块的能力放在可选插件中,我们需要安装rollup-plugin-commonjs。

代码语言:javascript复制
npm install -D rollup-plugin-commonjs
代码语言:javascript复制
//rollup.config.js
 plugins: [
       // …
    commonjs({
        include: ['node_modules/**']
    }),
    // …
 ]

7. PostCSS插件

再再再次运行rollup -c。

上一个Error解决了,我们来看下新的Error

代码语言:javascript复制
!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
node_modules/mobile-select/mobile-select.css (1:0)

Rollup本身不支持加载css组件,我们需要安装一个Rollup的PostCSS插件。

代码语言:javascript复制
npm install -D rollup-plugin-postcss
代码语言:javascript复制
//rollup.config.js
 plugins: [
       // …
    postcss({
        extensions: ['.css'],
    }),
    // …
 ]

最后一次运行rollup -c,可以了。

8. 压缩代码

打包出来的js文件比较大,我们用rollup-plugin-terser压缩下代码。

代码语言:javascript复制
npm install -D rollup-plugin-terser

在rollup.config.js的 插件列表加上terser()。

9. Rollup输出配置

打包的工作到这里就结束了,我们想要测试打包成果。

可能你还记得,我们打包输出的是CommonJS模块,因为rollup.config.js里面的output.format选项选择的是cjs。

可选的输出模块类型有下面这些:

  • amd – 异步模块定义,用于像 RequireJS 这样的模块加载器。
  • cjs – CommonJS,适用于 Node 和 Browserify/Webpack。
  • es – 将软件包保存为 ES 模块文件。
  • iife – 一个自动执行的功能,适合作为<script>标签。(如果要为应用程序创建一个捆绑包,您可能想要使用它,因为它会使文件大小变小。)
  • umd – 通用模块定义,以 amd,cjs 和 iife 为一体。

为了放在<script>中使用,方便测试,我们将打包格式改为umd。

同时,对于life和umd格式,我们需要指定输出模块名称,在js脚本中可以直接使用模块。

最终,我们的Rollup打包配置是这样的。

代码语言:javascript复制
//rollup.config.js
import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs'
import postcss from 'rollup-plugin-postcss';

export default {
    input: './src/main.js',
    output: {
        file: './dist/tip-city-selector.js',
        format: 'umd',
        name: 'CitySelector'
    },
    runtimeHelpers: false,
    plugins: [
        postcss({
            extensions: ['.css'],
        }),
        resolve({
            browser: true,
        }),
        commonjs({
            include: ['node_modules/**']
        }),
        babel({
            exclude: 'node_modules/**',
        }),
        terser(),
    ]
};

10. 发布npm

最后,如果对封装的组件有信心的话,我们可以把组件发布到npm

package.json的main属性指向包的出口,在我们的项目中,是"main": "dist/tip-city-selector.js"。

npm login登录,npm publish发布。

0. 参考文章

Rollup官方文档

你真的会用 Babel 吗?

rollup-plugin-babel

0 人点赞