Rollup基础入门

838 阅读7分钟

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

Rollup是一个JavaScript的模块化打包工具,可以帮助我们编译小的代码到一个大的、复杂的代码中,比如一个库或者一个应用程序

Rollup的定义、定位和webpack非常的相似

  1. Rollup也是一个模块化的打包工具,但是Rollup主要是针对ES Module进行打包的

    默认情况下rollup是无法对其它模块进行打包操作的,

    如果要对其它模块方式编写的模块进行打包,需要依赖对应的插件

  2. webpack通常可以通过各种loader处理各种各样的文件,以及处理它们的依赖关系

    rollup更多时候是专注于处理JavaScript代码的(当然也可以处理css、font、vue等文件)

  3. rollup的配置和理念相对于webpack来说,更加的简洁和容易理解

  4. 在早期webpack不支持tree shaking时,rollup具备更强的优势

  5. 在实际项目开发过程中,我们都会使用webpack(比如vue、react、angular项目都是基于webpack的)

    在对库文件进行打包时,我们通常会使用rollup(比如vue、react、dayjs源码本身都是基于rollup的);

使用

命令行使用

安装

# 全局安装
npm install rollup -g # 局部安装
npm install rollup -D

使用

 # 打包浏览器的库
npx rollup ./src/main.js -f iife -o dist/bundle.js

# 打包AMD的库
npx rollup ./src/main.js -f amd -o dist/bundle.js

# 打包CommonJS的库
npx rollup ./src/main.js -f cjs -o dist/bundle.js

# 打包通用的库(必须跟上name) --- UMD的库可以兼容AMD,CJS和浏览器
# 在兼容浏览器的时候需要将打包后的内容挂载到全局对象window上,所以需要指定挂载到window上的那个属性的名称(name)
# 类似于dayjs中的dayjs,lodash中的_
npx rollup ./src/main.js -f umd --name mathUtil -o dist/bundle.js

配置文件

我们可以将配置信息写到配置文件中rollup.config.js文件:

rollup.config.js

// 可以使用module.exports导出
// 但是rollup默认是对ESM进行打包的,所以多数情况下使用ESM的导出方式
export default {
  // 入口
  input: './src/main.js',

  // 出口
  output: {
    // 输出的文件需要在那个平台上运行
    format: 'umd',
    // 是umd就需要提供name
    name: 'main',
    // 输出到哪里
    file: './dist/main.umd.js'
  }
}

package.json

"scripts": {
  // rollup -c rollup.config.js 可以简写为 rollup -c 因为rollup.config.js是默认的配置文件
  // -c后可以跟自定义的配置文件 ---> rollup -c rollup.custom.config.js
  "build": "rollup -c"
}

可以对文件进行分别打包,打包出更多的库文件(用户可以根据不同的需求来引入)

export default {
  input: './src/main.js',

  // output的值可以设置为数组,从而打包出多个运行在不同平台上的文件
  output: [
    {
      format: 'umd',
      name: 'main',
      file: './dist/main.umd.js'
    },

    {
      format: 'amd',
      file: './dist/main.amd.js'
    },

    {
      format: 'cjs',
      file: './dist/main.cjs.js'
    },

    {
      format: 'es',
      file: './dist/main.es.js'
    },

    {
      format: 'iife',
      // 在浏览器端使用的时候,推荐将打包后的内容挂载到全局变量上,以便于调用者进行使用
      name: 'main',
      file: './dist/main.js'
    }
  ]
}

插件使用

rollup和别的构建工具一样可以使用对应的插件来扩展构建工具的功能,

可以在这里查看rollup官方提供的插件

使用CJS打包的库

rollup主要是对ESM进行打包处理的,默认情况下,rollup不能使用CJS编写的模块

# 如果需要在rollup中引入cjs编写的包,需要使用插件@rollup/plugin-commonj
npm install @rollup/plugin-commonjs -D
// 和webpack不同的是 rollup的插件导出的是一个个的函数
import commonjs from '@rollup/plugin-commonjs'

export default {
  input: './src/main.js',

  output: {
    format: 'umd',
    name: 'main',
    file: './dist/main.umd.js'
  },

  // 使用插件
  plugins: [
    commonjs()
  ]
}

cjs模块

// 这里模拟使用的第三方CJS模块
const name = 'foo'

module.exports = {
  name
}

使用CJS模块

// rollup默认可以解析js,json后缀的文件,所以可以省略对应的后缀名
import { name } from './foo/index'

// 在rollup中虽然存在插件可以帮助我们将CJS模块编写的包进行解析并引入
// 但是rollup不允许我们使用CJS的方式将对应的模块进行引入 ---> 也就是说rollup仅支持CJS的导出,不支持CJS的导入方式
// 因为rollup需要基于ESM进行tree shaking操作,所以对于我们自己编写的代码,rollup强制我们使用ESM的方式进行代码的编写
// const { name } = require('./foo/index')

console.log(name)

export const sum = (num1, num2) => num1 + num2

使用node_module下的第三方库

rollup在多数情况下是用来对第三方库进行打包的构建工具,所以默认情况下,如果我们使用了第三方库的时候,代码可以正常编译

但是对应的模块并不会被引入,而是需要我们自己在html文件中使用CDN的方式进行手动引入

例如:当我们使用lodash模块的时候,( 默认情况下,lodash模块是CJS方式导出的,所以需要先使用@babel/plugin-commonjs插件进行解析 )

import _ from 'lodash'

console.log(_.join([22, 33]))

编译后

// ...
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var ___default = /*#__PURE__*/_interopDefaultLegacy(_);

// 编译后的代码中并没有引入loadsh这个包
console.log(___default['default'].join([22, 33]));

// ...

所以此时直接运行编译打包后输出的main.js文件的时候,是会报错的

此时需要将html文件修改为

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 我们需要在这里手动引入对应的loadsh提供的cdn路径 -->
  <script src="../node_modules/lodash/lodash.min.js"></script>
  <script src="../dist/main.umd.js"></script>
</body>
</html>

但是实际这么做的时候,rollup是会报错的,因为默认情况下,rollup并无法解析node_modules下的模块,如果需要使用 ,需要安装

插件@rollup/plugin-node-resolve,这样rollup才可以正常对node_modules下的模块进行打包

import resolve from '@rollup/plugin-node-resolve'

plugins: [
  resolve()
]

但是此时会发现对应的模块都被引入进来了,但是实际的时候我们并不希望引入第三方库的代码

rollup.config.js

import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'

export default {
  input: './src/main.js',

  output: {
    format: 'umd',
    name: 'main',
    file: './dist/main.umd.js',

    // 指定全局的_来自于lodash模块
    globals: {
      'lodash': '_'
    }
  },

  // 将lodash的包从构建后的代码中抽离
  external: [
    'lodash'
  ],

  plugins: [
    commonjs(),
    resolve()
  ]
}

ES6转ES5

# 安装
npm i @rollup/plugin-babel -D
import babel from '@rollup/plugin-babel'

plugins: [
  babel({
    exclude: 'node_modules/**'
    // 在ES6转换为ES5的时候,会使用到一些helper函数
    // 如果在转换的过程中,出现多个重复的helper函数,那么会将这些重复的helper函数合并成一个
    babelHelpers: 'bundled'
  })
]

压缩代码

# 下面这个插件是第三方插件 --- 该插件内部使用的依旧是terser,只不过提供了其在rollup中的支持
npm i rollup-plugin-terser -D
// rollup-plugin-terser的导出使用的不是默认导出方式
import { terser } from 'rollup-plugin-terser'

plugins: [
  // rollup-plugin-terser提供了默认的terser配置
  terser()
]

处理其它文件

css文件

和在webpack中加载非js文件一样,webpack是依赖于各种各样的loader的,而rollup则是依赖于各种各样的plugins

为了在rollup中可以加载css文件,我们需要安装插件rollup-plugin-postcss

rollup-plugin-postcss默认集成了对scss、less、stylus的支持,在我们项目中,只要配置了rollup-plugin-postcss,就可以直接使用这些css预编译器,

# 安装
npm install rollup-plugin-postcss postcss -D

rollup.config.js

import postcss from 'rollup-plugin-postcss'

plugins: [
  // 插件的执行存在一定的先后顺序,所以postcss的执行应该放置在terser之前
  postcss(),
  terser()
]

vue文件

# 安装
npm i rollup-plugin-vue rollup-plugin-replace -D
import vue from 'rollup-plugin-vue'
import replace from 'rollup-plugin-replace'

plugins: [
  // commonjs插件在引入vue3的时候可能会出现问题
  // 所以需要在commonjs插件之前就解析vue的SFC文件
  vue(),
  commonjs(),
  resolve(),
  replace({
    // vue在处理的过程中,存在process.env.NODE_ENV,所以需要手动设置这些全局变量的值
    // rollup-plugin-replace的时候和webpack中definedPlugin插件的使用方式类似
    'process.env.NODE_ENV': JSON.stringify('development'),
    // 同样rollup-plugin-replace也可以用来注入全局变量
    ENV: JSON.stringify('development')
  }),
  babel({
    babelHelpers: 'bundled'
  }),
  postcss(),
  terser()
]

本地服务

# 安装
npm i rollup-plugin-serve -D

rollup.config.js

import serve from 'rollup-plugin-serve'

plugins: [
  serve({
    port: 8080,
    contentBase: '.' // 以什么目录作为项目根目录 --- . 表示的是当前目录
  })
]

live reload

# 安装
npm i rollup-plugin-livereload -D

package.json

"scripts": {
  "build": "rollup -c",
  "serve": "rollup -c -w" // 监听文件变化并实时进行编译
}

rollup.config.js

import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'

plugins: [
  serve({
    port: 8080,
    contentBase: '.'
  }),
  livereload()
]

区分环境

package.json

"scripts": {
  "build": "rollup -c --environment NODE_ENV:production",
   // --environment NODE_ENV:development配置的环境变量
   // 可以在rollup.config.js中通过process.env.NODE_ENV来获取具体的值
   "serve": "rollup -c -w --environment NODE_ENV:development"
}

rollup.config.js

import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'

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

import postcss from 'rollup-plugin-postcss'
import vue from 'rollup-plugin-vue'
import replace from 'rollup-plugin-replace'

import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'

// 获取传入的环境变量
const isDev = process.env.NODE_ENV === 'development'

const plugins = [
  vue(),
  commonjs(),
  resolve(),
  replace({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    ENV: JSON.stringify('development')
  }),
  babel({
    babelHelpers: 'bundled'
  }),
  postcss()
]

// 根据不同的环境变量,使用不同的插件
if (isDev) {
 plugins.push(...[
  serve({
    port: 8080,
    contentBase: '.'
  }),
  livereload()
 ])
} else {
  plugins.push(terser())
}

export default {
  input: './src/main.js',

  output: {
    format: 'umd',
    name: 'main',
    file: './dist/main.umd.js',

    globals: {
      'lodash': '_'
    }
  },

  external: [
    'lodash'
  ],

  plugins
}