库打包工具 Rollup

2,480 阅读5分钟

1. rollup核心思想和场景

1.1 认识rollup

  • 看一下官方对rollup的定义:

    • Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application.
    • Rollup是一个JavaScript的模块化打包工具,可以帮助我们编译小的代码到一个大的、复杂的代码中,比如一个库或者一个应用程序
  • Rollup的定义、定位和webpack非常的相似:

    • Rollup也是一个模块化的打包工具,但是Rollup主要是针对ES Module进行打包的
    • 另外webpack通常可以通过各种loader处理各种各样的文件,以及处理它们的依赖关系
    • rollup更多时候是专注于处理JavaScript代码的(当然也可以处理css、font、vue等文件)
    • rollup的配置和理念相对于webpack来说,更加的简洁和容易理解
    • 在早期webpack不支持tree shaking时,rollup具备更强的优势

1.2 应用场景

  • 目前webpack和rollup分别应用在什么场景呢?

    • 通常在实际项目开发过程中,都会使用webpack(比如react、angular项目都是基于webpack的
    • 在对库文件进行打包时,通常会使用rollup(比如vue、react、dayjs源码本身都是基于rollup的,Vite底层使用Rollup)

2. rollup命令行打包

  • 不同的执行环境使用不同命令
    # 打包浏览器的库
    npx rollup ./src/main.js -f iife -o dist/bundle.js
    
    # 打包AMD的库
    npx rollup ./src/main.js -f amd -o dist/bundle.js
    
    # 打包CommonJS的库 node环境
    npx rollup ./src/main.js -f cjs -o dist/bundle.js
    
    # 打包通用的库(必须跟上name)
    npx rollup ./src/main.js -f umd --name mathUtil -o dist/bundle.js
    

3. rollup配置文件编写

  • 将配置信息写到配置文件中rollup.config.js文件

    module.exports = {
      // 入口
      input: './lib/index.js',
      // 出口
      // output: {
      //   format: 'umd',
      //   name: 'ysUtils',
      //   file: './build/bundle.umd.js'
      // }
      // 打包不同格式
      output: [
        {
          format: 'umd',
          name: 'ysUtils',
          file: './build/bundle.umd.js'
        },
        {
          format: 'amd',
          file: './build/bundle.amd.js'
        },
        {
          format: 'cjs',
          file: './build/bundle.cjs.js'
        },
        {
          format: 'iife',
          name: 'ysUtils',
          file: './build/bundle.browser.js'
        }
      ]
    }
    
  • 打包文件

    • 执行脚本: npx rollup -c

4. rollup打包第三方库

  • 解决commonjs的问题

    • 默认lodash没有被打包是因为它使用commonjs, rollup默认情况下只会处理es module
    • 解决commonjs导出,es module导入
    • npm install @rollup/plugin-commonjs -D
  • 解决node_modules的问题

    • rollup 并不支持直接打包 node-modules 里面的内容,所以需要安装 rollup-plugin-node-resolve 插件
    • npm install @rollup/plugin-node-resolve -D
  • 配置文件

    const commonjs = require('@rollup/plugin-commonjs')
    const nodeResolve = require('@rollup/plugin-node-resolve')
    
    module.exports = {
      // 入口
      input: './lib/index.js',
      // 出口
      output: {
        format: 'umd',
        name: 'ysUtils',
        file: './build/bundle.umd.js',
        globals: {
          lodash: '_'
        }
      },
      external: ['lodash'],
      plugins: [
        commonjs(),
        nodeResolve(),
      ]
    }
    

5. rollup打包js文件

  • babel转换代码

    • babel.config.js配置文件
    • npm install @rollup/plugin-babel -D
  • terser压缩代码

    • npm install @rollup/plugin-terser -D
  • 配置文件

    const commonjs = require('@rollup/plugin-commonjs')
    const nodeResolve = require('@rollup/plugin-node-resolve')
    
    // 代码转换和压缩
    const { babel } = require('@rollup/plugin-babel')
    const terser = require('@rollup/plugin-terser')
    
    module.exports = {
      // 入口
      input: './lib/index.js',
      // 出口
      output: {
        format: 'umd',
        name: 'ysUtils',
        file: './build/bundle.umd.js',
        globals: {
          lodash: '_'
        }
      },
      external: ['lodash'],
      plugins: [
        commonjs(),
        nodeResolve(),
        babel({
          // polyfill
          babelHelpers: 'bundled',
          exclude: /node_modules/
        }),
        terser()
      ]
    }
    

6. rollup处理css文件

  • 处理css文件,可以使用postcss

    • npm install rollup-plugin-postcss postcss -D
    • npm install postcss-preset-env -D
  • 配置文件

    const commonjs = require('@rollup/plugin-commonjs')
    const nodeResolve = require('@rollup/plugin-node-resolve')
    
    // 代码转换和压缩
    const { babel } = require('@rollup/plugin-babel')
    const terser = require('@rollup/plugin-terser')
    
    const html = require('@rollup/plugin-html')
    const postcss = require('rollup-plugin-postcss')
    
    module.exports = {
      // 入口
      input: './src/index.js',
      // 出口
      output: {
        format: 'umd',
        name: 'ysUtils',
        file: './build/bundle.umd.js',
        globals: {
          lodash: '_'
        }
      },
      // external: ['lodash'],
      plugins: [
        commonjs(),
        nodeResolve(),
        babel({
          // polyfill
          babelHelpers: 'bundled',
          exclude: /node_modules/
        }),
        terser(),
        html(),
        postcss({ plugins: [require('postcss-preset-env')]}),
      ]
    }
    

7. rollup处理vue文件

  • 处理vue文件需要使用rollup-plugin-vue插件:

    • npm install rollup-plugin-vue @vue/compiler-sfc -D
  • 打包后vue项目会报错

    image.png

    • 因为在打包的vue代码中,用到 process.env.NODE_ENV,所以可以使用一个插件 rollup-plugin-replace 设置它对应的值:
      • npm install @rollup/plugin-replace -D
  • 注意:插件有先后顺序,vue()放到 postcss() 前面,否则后面修改css内容会报错

  • 配置文件

    const commonjs = require('@rollup/plugin-commonjs')
    const nodeResolve = require('@rollup/plugin-node-resolve')
    
    // 代码转换和压缩
    const { babel } = require('@rollup/plugin-babel')
    const terser = require('@rollup/plugin-terser')
    
    const html = require('@rollup/plugin-html')
    const postcss = require('rollup-plugin-postcss')
    const vue = require('rollup-plugin-vue')
    // 注入变量
    const replace = require('rollup-plugin-replace')
    
    module.exports = {
      // ...
      plugins: [
        vue(),
        commonjs(),
        nodeResolve(),
        babel({
          // polyfill
          babelHelpers: 'bundled',
          exclude: /node_modules/
        }),
        terser(),
        html(),
        postcss({ plugins: [require('postcss-preset-env')]}),
        replace({
          'process.env.NODE_ENV': JSON.stringify('production')
        })
      ]
    }
    

8. rollup搭建本地服务器

  • 第一步:使用rollup-plugin-serve搭建服务

    • npm install rollup-plugin-serve -D
  • 第二步:当文件发生变化时,自动刷新浏览器

    • npm install rollup-plugin-livereload -D
  • 第三步:启动时,开启文件监听

    • npx rollup -c -w
  • 配置文件

    const commonjs = require('@rollup/plugin-commonjs')
    const nodeResolve = require('@rollup/plugin-node-resolve')
    
    // 代码转换和压缩
    const { babel } = require('@rollup/plugin-babel')
    const terser = require('@rollup/plugin-terser')
    
    const html = require('@rollup/plugin-html')
    const postcss = require('rollup-plugin-postcss')
    const vue = require('rollup-plugin-vue')
    // 注入变量
    const replace = require('@rollup/plugin-replace')
    const serve = require('rollup-plugin-serve')
    const livereload = require('rollup-plugin-livereload')
    
    module.exports = {
      // ...
      plugins: [
        commonjs(),
        nodeResolve(),
        babel({
          // polyfill
          babelHelpers: 'bundled',
          exclude: /node_modules/
        }),
        terser(),
        html(),
        vue(),
        // postcss({ plugins: [require('postcss-preset-env')]}),
        postcss(),
        replace({
          'process.env.NODE_ENV': JSON.stringify('production'),
          preventAssignment: true
        }),
        serve({
          port: 8080,
          // open: true,
          // 依赖的文件夹
          contentBase: './build'
        }),
        livereload()
      ]
    }
    

9. rollup区分不同环境

  • 在package.json中创建脚本
    "scripts": {
      "build": "rollup -c --environment NODE_ENV:production",
      "serve": "rollup -c --environment NODE_ENV:development -w"
    },
    
  • 配置文件
    const commonjs = require('@rollup/plugin-commonjs')
    const nodeResolve = require('@rollup/plugin-node-resolve')
    
    // 代码转换和压缩
    const { babel } = require('@rollup/plugin-babel')
    const terser = require('@rollup/plugin-terser')
    
    const html = require('@rollup/plugin-html')
    const postcss = require('rollup-plugin-postcss')
    const vue = require('rollup-plugin-vue')
    // 注入变量
    const replace = require('@rollup/plugin-replace')
    const serve = require('rollup-plugin-serve')
    const livereload = require('rollup-plugin-livereload')
    
    const isProduction = process.env.NODE_ENV === 'production'
    const plugins = [
      commonjs(),
      nodeResolve(),
      babel({
        // polyfill
        babelHelpers: 'bundled',
        exclude: /node_modules/
      }),
      html(),
      vue(),
      // postcss({ plugins: [require('postcss-preset-env')]}),
      postcss(),
      replace({
        'process.env.NODE_ENV': JSON.stringify('production'),
        preventAssignment: true
      })
    ]
    
    if(isProduction) {
      plugins.push(terser())
    } else {
      const extraPlugins = [
        serve({
          port: 8080,
          // open: true,
          contentBase: './build'
        }),
        livereload()
      ]
      plugins.push(...extraPlugins)
    }
    
    module.exports = {
      // 入口
      input: './src/index.js',
      // 出口
      output: {
        format: 'umd',
        name: 'ysUtils',
        file: './build/bundle.umd.js',
        globals: {
          lodash: '_'
        }
      },
      // external: ['lodash'],
      plugins: plugins
    }