Vite & Rollup 指南

385 阅读7分钟

第一部分:基础概念篇

1.1 Rollup

Rollup是一个专注于ES模块的JavaScript打包工具,由Rich Harris创建于2015年。它的核心理念是利用ES模块的静态结构进行Tree Shaking,生成更简洁、更高效的代码。

Rollup的核心特点:

  • 📦 专注ES模块:原生支持ESM,打包产物干净利落
  • ✂️ 强大的Tree Shaking:自动移除未使用的代码
  • 🔌 丰富的插件生态:6年多的积累,npm年下载量上亿次
  • 🎯 适合库开发:Vue、React等主流框架都在用Rollup打包
// rollup.config.js 基础示例
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import { terser } from 'rollup-plugin-terser'

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm' // 输出格式:esm、cjs、umd等
  },
  plugins: [
    resolve(), // 解析node_modules
    commonjs(), // 转换CommonJS
    terser() // 代码压缩
  ]
}

1.2 Vite

Vite(法语"快速"的意思)是尤雨溪团队于2020年发布的新一代前端构建工具。它的最大特点是利用浏览器原生ESM支持,实现开发环境no-bundle

Vite的核心特点:

  • ⚡ 极速冷启动:毫秒级启动,与项目规模无关
  • 🔥 即时热更新:HMR速度快且稳定
  • 🛠️ 开箱即用:内置TypeScript、JSX、CSS预处理器
  • 🤝 双引擎架构:开发用esbuild,生产用Rollup
// vite.config.js 基础示例
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    outDir: 'dist',
    rollupOptions: {
      // 这里可以配置Rollup选项
      input: 'index.html'
    }
  }
})

1.3 两者的核心区别

维度RollupVite
定位打包工具构建工具(包含开发服务器)
核心优势Tree Shaking、产物纯净开发体验、启动速度
适用场景库/工具开发应用开发
HMR支持❌ 不支持✅ 支持(基于ESM)
配置复杂度中等低(开箱即用)
生态成熟稳定快速发展中

一句话总结

  • Vite是构建工具(包含开发服务器和打包功能)
  • Rollup是打包工具(专注于打包优化)
  • Vite的生产打包功能正是建立在Rollup之上的

1.4 esbuild

esbuild 是一个用 Go语言 编写的下一代前端构建工具,由Evan Wallace(前Figma CTO)于2020年创建。它的核心特点就是一个字:!快到什么程度呢?

性能对比(打包Three.js)

工具语言耗时相对速度
Webpack 5JavaScript23.5s1倍(基准)
Rollup 3JavaScript18.2s1.3倍
Parcel 2JavaScript+Rust14.1s1.7倍
esbuildGo0.3s78倍 🚀

关键数据:esbuild比Webpack快 78倍!这就是Vite选择它作为开发环境引擎的根本原因。

esbuild局限性

限制项说明影响程度
代码分割不支持动态导入的精细控制⭐⭐⭐
ES5降级不能生成ES5代码⭐⭐⭐
TS语法限制不支持const enum⭐⭐
HMR支持无内置HMR方案⭐⭐
产物操作无法像Rollup那样精细控制输出⭐⭐

第二部分:双引擎架构深度解析

2.1 Vite的双引擎设计哲学

Vite最精妙的设计在于:开发环境与生产环境采用不同的工具链,各取所长。

image.png

2.2 esbuild在Vite中的三重角色

角色一:依赖预构建(Bundler)

当Vite启动时,它会扫描项目依赖,用esbuild将数百个分散的依赖模块打包成单个ESM文件。

// 预构建前
import { ref } from 'vue' // vue有上百个文件

// 预构建后
// node_modules/.vite/vue.js - 单个ESM文件

为什么要预构建?

  1. 解决CommonJS兼容性:很多依赖是CommonJS格式
  2. 减少HTTP请求:避免浏览器发送上百个请求
  3. 统一ESM格式:确保所有依赖都能被浏览器识别

角色二:单文件编译(Transformer)

Vite使用esbuild将TS、JSX等文件转换为JS,替代了传统的Babel或TSC。

性能对比(编译50MB纯代码文件):

  • Babel:约40秒
  • TSC:约60秒
  • SWC:约2秒
  • esbuild:约0.3秒 ⚡

局限性:esbuild不做类型检查,只抹除类型。这就是为什么vite build之前要执行tsc

角色三:代码压缩(Minifier)

从Vite 2.6开始,默认使用esbuild进行生产环境的JS和CSS压缩。

压缩性能对比(echarts 3.2MB):

  • Terser:8798ms
  • esbuild:361ms(快24倍)

2.3 Rollup在Vite中的核心地位

为什么生产环境不用esbuild?

esbuild虽然快,但有四个致命缺陷:

缺陷说明影响
不支持ES5降级无法输出ES5代码低端浏览器无法运行
语法限制不支持const enum某些TS语法会报错
无产物操作接口没有renderChunk等钩子无法精细控制输出
拆包策略固定不支持自定义Code Splitting优化灵活性不足

而Rollup恰恰在这些方面表现出色,经过6年多的迭代,产物质量和稳定性都经得起考验。

Rollup在Vite中的三大优化

  1. CSS代码分割:异步模块中的CSS自动抽取成独立文件,提高缓存复用率

  2. 自动预加载:为入口chunk的依赖自动生成<link rel="modulepreload">

    <head>
      <script type="module" src="/assets/index.123abc.js"></script>
      <link rel="modulepreload" href="/assets/vendor.456def.js">
    </head>
    
  3. 异步Chunk加载优化:请求A的同时自动预加载公共依赖C,节省网络开销。

2.4 对比

Rollup和Webpack对比

  1. 设计理念与功能
  • Webpack

    • 全能型的模块打包工具:支持 JavaScript、CSS、HTML、图片等各种静态资源
    • 丰富的Loader机制:可以转换非 JavaScript 资源
    • 代码分割 Code Splitting:路由懒加载代码块,提升页面加载速度
    • 热模块替换 HMR:开发过程中实时刷新
    • 配合Plugin系统实现复杂的构建流程定制
  • Rollup

    • Rollup专注于JavaScript模块的打包和优化,专注于ES6模块规范
    • 严格静态分析,出色的tree-shaking能力
    • 输出的bundle倾向于更小、更纯净
  1. 适用场景
  • Webpack

    • 大型SPA或企业级Web应用等需要处理多种资源和复杂的构建流程
    • 需要做代码分割和按需加载的场景
    • 有大量第三方库依赖和复杂业务逻辑的项目
  • Rollup

    • 开发和发布独立的JavaScript库或组件,特别是那些遵循ES6模块规范的库
    • 项目主要关注打包后代码大小和纯净性,而非处理大量非JS资源
  1. 如何选择
  • 选择Webpack

    • 当项目同时包含多个资源类型
    • 当项目需要实现复杂的代码分割和动态加载策略
    • 当项目规模较大,需要高度定制化构建流程时
  • 选择Rollup

    • 当项目主要是编写一个独立的、面向外部发布的JavaScript库时
    • 当需要最大程度地优化代码大小,去除无用模块时
    • 当项目不涉及过多的非JS资源,只需要专注JS模块打包优化时

使用Rollup打包库本身,使用Webpack来整合应用的所有资源并进行优化

Vite与webpack对比

  • webpack打包项目:缓慢的服务器启动、缓慢的更新

image.png

当使用webpack打包项目时,webpack会根据配置文件中的入口文件entry,分析项目中所有的依赖关系,然后打包成一个文件bundle.js并交给浏览器去加载渲染。

  • Vite打包项目:更快的服务器启动和更新速度

image.png

<script type="module">中,浏览器遇到内部的import引用时,会自动发起http请求,去加载对应的模块。使用vite运行项目时,首先会用esbuild进行预构建,将所有模块转换为es module,不需要对整个项目进行编译打包,而是在浏览器需要加载某个模块时,拦截浏览器发出的HTTP请求,根据请求进行按需编译,然后返回给浏览器。

第三部分:性能优化策略

3.1 开发环境优化

依赖预构建优化

// vite.config.js
export default {
  optimizeDeps: {
    // 强制包含某些依赖
    include: [
      'lodash-es',
      'axios > follow-redirects' // 嵌套依赖
    ],
    
    // 排除不需要预构建的包
    exclude: ['your-large-lib'],
    
    // esbuild配置
    esbuildOptions: {
      target: 'es2020',
      supported: {
        'top-level-await': true
      }
    }
  }
}

预热常用文件

export default {
  server: {
    warmup: {
      // 启动时提前转换这些文件,避免请求瀑布
      clientFiles: [
        './src/main.ts',
        './src/App.vue',
        './src/router/index.ts'
      ]
    }
  }
}

3.2 生产环境优化

代码分割策略

export default {
  build: {
    rollupOptions: {
      output: {
        // 手动分块
        manualChunks: {
          // 框架代码单独打包
          'vendor': ['vue', 'vue-router', 'pinia'],
          // UI库单独打包
          'ui': ['element-plus', '@vueuse/components'],
          // 工具库单独打包
          'utils': ['lodash-es', 'dayjs', 'axios']
        },
        
        // 文件命名规则
        entryFileNames: 'assets/[name]-[hash].js',
        chunkFileNames: 'assets/[name]-[hash].js',
        assetFileNames: 'assets/[name]-[hash].[ext]'
      }
    },
    
    // 块大小警告阈值
    chunkSizeWarningLimit: 1000,
    
    // 压缩配置
    minify: 'terser', // 或 'esbuild'
    terserOptions: {
      compress: {
        drop_console: true, // 移除console
        drop_debugger: true
      }
    }
  }
}

CSS优化

export default {
  css: {
    // CSS模块化配置
    modules: {
      localsConvention: 'camelCase',
      scopeBehaviour: 'local',
      generateScopedName: '[name]__[local]__[hash:5]'
    },
    
    // 预处理器配置
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`
      }
    },
    
    // PostCSS配置
    postcss: {
      plugins: [
        require('autoprefixer'),
        require('cssnano')({ preset: 'default' })
      ]
    }
  }
}

3.3 构建分析

// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'

export default {
  plugins: [
    visualizer({
      filename: 'dist/stats.html',
      open: true, // 构建后自动打开
      gzipSize: true,
      brotliSize: true
    })
  ]
}