Vite 生产构建的 Rollup 配置调优:插件顺序、输出格式与资源内联
2026/8/9 0:46:14 网站建设 项目流程

Vite 生产构建的 Rollup 配置调优:插件顺序、输出格式与资源内联

一、Vite 生产构建的底层架构

Vite 的生产构建本质上是 Rollup 的一次受控执行。Vite 在 Rollup 之上叠加了两层封装:第一层是插件兼容层(将 Vite 插件格式转换为 Rollup 插件接口),第二层是配置聚合层(将vite.config.ts中的分散配置项映射到 Rollup 的OutputOptionsInputOptions等结构)。

理解这个映射关系是调优的前提。很多开发者直接修改vite.config.tsbuild.rollupOptions字段,却不知道 Vite 内部已经注入了隐含默认值——比如output.format默认为esmoutput.manualChunks默认使用 Vite 的自动分割策略。盲目覆盖可能导致内建优化失效。

具体而言,配置流程始于vite.config.ts,经由 Vite 配置聚合层处理后,分别映射为 Rollup 的InputOptions(负责插件排序与入口定义)和OutputOptions(负责输出格式与分块策略)。这两者共同驱动 Rollup 执行,最终生成 ESM/CJS/IIFE 格式的产物及静态资源。

二、插件顺序:构建链路的性能瓶颈

Rollup 插件按注册顺序执行,钩子分为build阶段(解析、转换)和output阶段(生成、写入)。插件顺序直接影响两个指标:构建耗时与产物体积。

2.1 构建阶段插件的排序原则

// vite.config.ts — 正确的插件排序示范 import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react';

import legacy from '@vitejs/plugin-legacy';
import compression from 'vite-plugin-compression';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
plugins: [
// 1. 语法转换类插件——最先执行,确保后续插件能处理标准 AST
react(),

// 2. 分析类插件——在转换后执行,读取标准化 AST visualizer({ open: false, gzipSize: true }), // 3. 产物后处理类插件——最后执行 legacy({ targets: ['defaults', 'not IE 11'] }), compression({ algorithm: 'gzip', threshold: 10240 }),

],
build: {
rollupOptions: {
// 以下配置直接映射到 Rollup OutputOptions
output: {
format: 'esm',
entryFileNames: 'assets/[name]-[hash].js',
chunkFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]',
},
},
},
});

排序原则总结: - **语法转换优先**(babel/swc/ts):后续插件依赖标准化 AST - **代码分析居中**(visualizer/coverage):需要读取最终 AST - **产物后处理末尾**(compression/legacy):操作最终输出 错误排序的典型后果:将 compression 放在 react 之前,会导致 JSX 未转换就被压缩,产物不可用。 ### 2.2 插件钩子冲突检测 ```typescript /** * 检测插件钩子注册顺序冲突 * build 钩子必须先于 output 钩子 */ function validatePluginOrder(plugins: Plugin[]): string[] { const warnings: string[] = []; const buildHooks = ['resolveId', 'load', 'transform']; const outputHooks = ['renderChunk', 'writeBundle', 'generateBundle']; let lastBuildIndex = -1; plugins.forEach((plugin, index) => { const hasBuild = Object.keys(plugin).some(k => buildHooks.includes(k)); const hasOutput = Object.keys(plugin).some(k => outputHooks.includes(k)); if (hasOutput && !hasBuild && index < lastBuildIndex) { warnings.push( `插件 "${plugin.name}" 仅含 output 钩子却位于 build 钩子插件之前,建议调整顺序` ); } if (hasBuild) { lastBuildIndex = index; } }); return warnings; }

三、输出格式选择与多格式配置

3.1 三种输出格式的适用场景

格式适用场景Tree-shaking动态导入浏览器直接加载
ESM现代浏览器、构建工具链消费完整支持完整支持支持
CJSNode.js 服务端、旧版工具部分支持需 polyfill不支持
IIFECDN 直接引入、非构建场景不支持不支持支持

3.2 多格式输出配置

当库需要同时支持 ESM 和 CJS 消费者时:

import { defineConfig } from 'vite'; export default defineConfig({ build: { lib: { entry: 'src/index.ts', name: 'MyLib', fileName: (format) => `my-lib.${format}.js`, }, rollupOptions: { // 多格式输出:每个格式独立配置 output: [ { format: 'esm', // ESM 输出保留动态导入,不做 polyfill preserveModules: true, preserveModulesStructure: true, entryFileNames: '[name].mjs', }, { format: 'cjs', // CJS 输出需处理动态导入为同步 require dynamicImportInCjs: true, entryFileNames: '[name].cjs', }, ], // 外部化 Node.js 内置模块 external: ['fs', 'path', 'crypto'], }, }, });

注意:preserveModules: truemanualChunks互斥。前者按源文件结构分割模块,后者按自定义逻辑合并——两者不能同时生效。

四、资源内联策略:权衡请求数与首屏体积

4.1 小资源内联的阈值决策

CSS 小文件、SVG 图标、JSON 配置等小型资源可以内联到 JS bundle 中,减少 HTTP 请求。但内联会增加 JS 体积,阻塞主线程解析。

/** * 资源内联阈值计算器 * 根据资源体积与当前 bundle 大小动态决策 */ function calculateInlineThreshold( resourceSize: number, bundleSize: number, httpCostMs: number = 50 // 单次额外请求的延迟估算 ): { shouldInline: boolean; reason: string } { // 内联后 JS 解析延迟增加估算:1KB ≈ 1ms(移动端) const parseCostMs = resourceSize / 1024; const inlineOverheadMs = parseCostMs; // 外联的额外 HTTP 请求成本 const externalCostMs = httpCostMs; if (resourceSize < 4096) { // 小于 4KB:内联收益明确 return { shouldInline: true, reason: '体积 < 4KB,请求节省优先' }; } if (inlineOverheadMs < externalCostMs) { return { shouldInline: true, reason: `内联解析延迟 ${inlineOverheadMs}ms < 外联请求延迟 ${externalCostMs}ms` }; } return { shouldInline: false, reason: `内联解析延迟 ${inlineOverheadMs}ms > 外联请求延迟 ${externalCostMs}ms` }; }

4.2 Vite 内联配置实践

// vite.config.ts — 资源内联精细化配置 export default defineConfig({ build: { // 小于 4KB 的资源内联为 base64/data URI assetsInlineLimit: 4096, // CSS 内联策略:开发环境外联(便于 HMR),生产环境内联 cssCodeSplit: true, rollupOptions: { output: { // 手动分块:将 runtime 与业务代码分离 manualChunks(id) { if (id.includes('node_modules')) { // 第三方依赖按包名分块 const match = id.match(/node_modules\/([^/]+)/); if (match) { const pkgName = match[1]; // 大型框架单独分块 if (['react', 'react-dom', 'vue'].includes(pkgName)) { return `vendor-${pkgName}`; } return 'vendor-misc'; } } }, }, }, }, });

五、总结

Vite 生产构建调优的核心认知是:Vite 并非"零配置即可用"的黑箱,而是 Rollup 的受控封装。调优的三个关键维度:

  • 插件顺序:语法转换 → 代码分析 → 产物后处理,逆序会导致构建失败或产物异常
  • 输出格式:ESM 优先(tree-shaking 完整),CJS 兼容(服务端消费),IIFE 仅用于 CDN 直接引入
  • 资源内联:4KB 阈值是经验起点,实际决策应对比内联解析延迟与外联请求延迟

配置调优不是一次性动作。随着依赖版本升级、业务模块增长,分块策略和内联阈值需要定期重新评估。rollup-plugin-visualizer 是持续监控产物体积的必备工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询