[踩坑] Rollup 打包 Vue 组件库项目 完整配置

9,796 阅读4分钟

前言

最近接到需求,需要将某个开源项目二次开发,提供给几个项目用。经过比对后决定使用Rollup来打包。 这篇文章记录下过程中踩的一些坑和一些优化项。

安装和配置

安装rollup

yarn global add rollup
// 或
npm i -g rollup

创建 rollup.config.js

// rollup.config.js
const config = {
  input: './src/index.js',  // 入口文件
  external: ['lodash', 'jquery'],  // 排除的依赖包
  output: {
    name: 'index',
    file: 'dist/index.js', // 打包后的index文件
    format: 'umd',       // umd格式 可换成 iife
    globals: {
      vue: 'Vue',
      jquery: '$'
    },
    inlineDynamicImports: true
  }
}


export default config

说明:

  • external可以排除不需要打包的依赖,比如peerDependencies中的依赖,然后需要去globals中声明他们的全局变量,比如项目中用jquery会使用$变量。
  • umd是一种通用格式,iife是给浏览器运行的。

Rollup 插件

需要装哪些插件

提起插件真的令人头疼,生态里各种插件五花八门,用的时候要时刻警惕一些坑和版本问题。

rollup 打包完整的 vue 项目需要用到大量的插件,咱们先分个类

基本的工程化插件

  • rollup-plugin-node-resolve 帮助 rollup 识别外部模块
  • rollup-plugin-vue vue2.x使用5.x版本 vue3使用6.x版本
  • rollup-plugin-babel babel插件 将es6+转为es5
  • rollup-plugin-copy 直接复制静态文件
  • rollup-plugin-terser 压缩代码
  • @rollup/plugin-commonjs 将commonjs模块转为es模块
  • @rollup/plugin-alias 路径别名
// rollup.config.js

import resolve from 'rollup-plugin-node-resolve'
import vue from 'rollup-plugin-vue'
import babel from 'rollup-plugin-babel'
import commonjs from '@rollup/plugin-commonjs'
import alias from '@rollup/plugin-alias'
import { terser } from 'rollup-plugin-terser'
import path from 'path'

const config = {
  ... 其他配置
  plugins: [
    resolve({
      extensions: ['.vue'], // 无后缀名引用时,需要识别 .vue 文件
      exclude: '**/node_modules/**'  // 排除node_modules
    }),
    alias({
      entries: [
        {
          find: 'demo-lib', // 别名名称,作为依赖项目需要使用项目名
          replacement: path.resolve(__dirname, 'src'), 
          customResolver: resolve({
            extensions: ['.js', '.jsx', '.vue', '.sass', '.scss']
          })
        }
      ]
    }),
    // 将不需要编译的静态文件直接复制到dist
    copy({
      targets: [
        { src: 'public/tinymce/*', dest: 'dist/tinymce' }
      ]
    }),
    vue({
      css: true,
      compileTemplate: true
    }),
    babel({  // 排除node_modules
      exclude: 'node_modules/**'
    }),
    commonjs(),
    terser()
  ]
}


export default config

打包css、图片、字体的插件

  • @rollup/plugin-image 识别图片文件
  • rollup-plugin-svg 识别svg文件
  • rollup-plugin-require-context 支持 require.context 语法
  • rollup-plugin-url 这里用来识别字体
  • rollup-plugin-postcss 识别css和预处理文件

Tip: 如果使用了scssrollup-plugin-postcss需要配合node-sass使用

// rollup.config.js

import image from '@rollup/plugin-image'
import postcss from 'rollup-plugin-postcss'
import svg from 'rollup-plugin-svg'
import url from 'rollup-plugin-url'
import sass from 'node-sass'

const isProd = process.env.BUILD === 'production' // 稍后在命令行说明

const config = {

  ... 其他配置
  plugins: [
      ... 其他配置
      
      postcss({
          extract: true,
          minimize: isProd, // 生产环境开启压缩
          extensions: ['.css', '.scss'], // 识别css和scss文件
          // 在打包过程中需要配合 node-sass 使用
          process: function(context, payload) { 
            return new Promise((resolve, reject) => {
              sass.render({
                file: context
              }, function(err, result) {
                if (!err) {
                  resolve(result)
                } else {
                  reject(err)
                }
              })
            })
          }
        }), 
        url({
          include: ['**/*.woff', '**/*.ttf'], // 打包字体为base64
          limit: Infinity
        }),
        image(),
        svg(),
        requireContext()
  ]
}


export default config

到这里,完整的 vue 工程的rollup打包配置已经有了,但是对开发环境很不友好,打包一次要一分多钟,加上主项目热更新要再等一分多钟,太浪费时间了。

优化项,打包加速🚀

一番网上冲浪过后,找到了一些热更新和优化打包的方案。

  1. 开启热更新,使用rollup-plugin-livereload插件
  2. watch模式打包时排除第三方依赖,使用rollup-plugin-peer-deps-external
  3. 关闭sourceMap
  4. 配置babel为runtime模式,不打包babel的helpers,前提安装@babel/runtime
import livereload from 'rollup-plugin-livereload'
import peerDepsExternal from 'rollup-plugin-peer-deps-external'
import commonjs from '@rollup/plugin-commonjs'

const isProd = process.env.BUILD === 'production' // 稍后在命令行说明

const config = {
  ... 其他配置
  
  external: ['lodash', 'jquery', /@babel\/runtime/],
  plugins: [
      ... 其他配置
      
      livereload(),
      commonjs({
          sourceMap: false
      }),
      // 该插件默认排除peerDependencies中的依赖
      peerDepsExternal({ 
          includeDependencies: !isProd  // 开启该选项,排除dependencies
      })
      
  ]
}

export default config

完整配置

// rollup.config.js
import resolve from 'rollup-plugin-node-resolve'
import vue from 'rollup-plugin-vue'
import babel from 'rollup-plugin-babel'
import commonjs from '@rollup/plugin-commonjs'
import image from '@rollup/plugin-image'
import alias from '@rollup/plugin-alias'
import requireContext from 'rollup-plugin-require-context'
import postcss from 'rollup-plugin-postcss'
import svg from 'rollup-plugin-svg'
import url from 'rollup-plugin-url'
import path from 'path'
import sass from 'node-sass'
import { terser } from 'rollup-plugin-terser'
import livereload from 'rollup-plugin-livereload'
import copy from 'rollup-plugin-copy'
import peerDepsExternal from 'rollup-plugin-peer-deps-external'

const isProd = process.env.BUILD === 'production'

const config = {
  input: './src/index.js',
  external: ['lodash', 'jquery', /@babel\/runtime/],
  output: {
    name: 'index',
    file: 'dist/index.js',
    format: 'umd',
    globals: {
      vue: 'Vue',
      jquery: '$'
    },
    inlineDynamicImports: true
  },
  plugins: [
    resolve({
      extensions: ['.vue'],
      exclude: '**/node_modules/**'
    }),
    alias({
      entries: [
        {
          find: 'demo-lib',
          replacement: path.resolve(__dirname, 'src'),
          customResolver: resolve({
            extensions: ['.js', '.jsx', '.vue', '.sass', '.scss']
          })
        }
      ]
    }),
    copy({
      targets: [
        { src: 'public/tinymce/*', dest: 'dist/tinymce' }
      ]
    }),
    vue({
      css: true,
      compileTemplate: true
    }),
    babel({
      exclude: 'node_modules/**'
    }),
    postcss({
      extract: true,
      minimize: isProd,
      extensions: ['.css', '.scss'],
      process: function(context, payload) {
        return new Promise((resolve, reject) => {
          sass.render({
            file: context
          }, function(err, result) {
            if (!err) {
              resolve(result)
            } else {
              reject(err)
            }
          })
        })
      }

    }),
    url({
      include: ['**/*.woff', '**/*.ttf'],
      limit: Infinity
    }),
    commonjs({
      sourceMap: false
    }),
    image(),
    svg(),
    requireContext(),
    terser(),
    peerDepsExternal({
      includeDependencies: !isProductionEnv
    })
  ]
}

if (!isProd) {
  config.plugins.push(livereload())
}

export default config

命令行

  • node --max-old-space-size=4096给node开启大内存 避免打包时内存不够
  • node_modules/rollup/dist/bin/rollup -c直接用本地安装的rollup,避免部署时某个服务器没有全局安装rollup
  • dev 模式开启 -w 监听文件动态打包
  • BUILD:development 配置环境变量 代码里从process.env.BUILD取到
{
  "main": "dist/index.js",
  "scripts": {
    "dev": "node --max-old-space-size=4096 node_modules/rollup/dist/bin/rollup -c -w --environment BUILD:development",
    "build": "node --max-old-space-size=4096 node_modules/rollup/dist/bin/rollup -c --environment BUILD:production"
  }
}

参考文章

# 彻底学会rollup打包vue组件,逐步实现组件库打包

# 用rollup打包vue组件库

# rollup打包加速