打包优化指南

本文提供一套完整的 Vite 构建优化实践方案,涵盖:

  • 构建体积分析
  • 图像资源压缩
  • 静态资源 Gzip/Brotli 压缩
  • 第三方依赖分包策略
  • 实际部署注意事项

适用于中大型 Vue/React 项目,帮助开发者显著减少首屏加载时间,提升 Lighthouse 分数。

1. 打包分析

安装:npm i rollup-plugin-visualizer -D

配置到plugins中:

import { visualizer } from 'rollup-plugin-visualizer';

export const setupVitePlugins = (): PluginOption[] => {
  const isProd = process.env.NODE_ENV === "production";

  const plugins:PluginOption[] = [ ]

  // 打包分析(生产环境可关闭)
  if (isProd) {
    plugins.push(
      // 生成分析报告
      visualizer({
        open: false, // 构建完成后自动打开报告页面
        filename: "stats.html", // 报告文件路径
        gzipSize: true, // 显示 gzip 压缩后的体积
        brotliSize: true, // 显示 brotli 压缩后的体积
      }),
    );
  }

  return plugins
}

构建完成后执行:npx serve -s dist,

然后访问本地服务并手动打开 dist/stats.html,或设置 open: true 自动弹出浏览器窗口。

2. 图像优化

详情见vite-plugin-image-optimizer

根据项目需求选择安装以下工具:

# 支持 PNG/JPG/WebP 等栅格图像压缩
npm i -D vite-plugin-image-optimizer sharp

# 支持 SVG 优化(可选)
npm i -D svgo

说明:

  • 使用SVGO优化 SVG 资源并传递自定义配置
  • 使用Sharp.js优化栅格资源(png、jpeg、gif、tiff、webp、avif),并可选择为每种扩展类型传递自定义配置。默认无损压缩(如PNG/JPEG质量设为100

配置

import { ViteImageOptimizer } from "vite-plugin-image-optimizer";

export const setupVitePlugins = (): PluginOption[] => {
  const plugins:PluginOption[] = [
    // 图片优化
    ViteImageOptimizer({
      // 针对不同格式的压缩参数
      png: { quality: 80 }, 
      jpeg: { quality: 80 },
      jpg: { quality: 80 },
      webp: { quality: 80, lossless: false }, // 转换WebP,非无损模式
      // 可选:排除node_modules或指定小图
      exclude: ['node_modules'],  // 避免误处理第三方库中的图片
      // // 可选:仅处理特定路径
      // include: ['src/assets/**/*.{jpg,jpeg,png,svg,webp}'],
    }),
  ]
  return plugins
}

3. 资源压缩

安装vite-plugin-compression2npm i -D vite-plugin-compression2

配置:

import { compression, defineAlgorithm } from "vite-plugin-compression2";

export const setupVitePlugins = (): PluginOption[] => {
  const plugins:PluginOption[] = [
    compression({
      // 同时生成 gzip 和 brotli 文件
      algorithms: [
        'gzip',
        'brotliCompress'
        // defineAlgorithm('gzip', { level: 9 }), // 自定义算法与压缩级别
      ],
      // 只压缩大于 1KB 的文件
      threshold: 1024,
      // 若压缩后更大则跳过
      skipIfLargerOrEqual: true,
      // 是否原始文件( 设为true时,要求服务端支持)
      deleteOriginalAssets: false,
    }),
  ]
  return plugins
}

选项

参数 类型 默认值 描述
include string | RegExp | Array<string | RegExp> /\.(html|xml|css|json|js|mjs|svg|yaml|yml|toml)$/ 包含符合任意这些条件的资源文件
exclude string | RegExp | Array<string | RegExp> - 排除符合任意这些条件的资源文件 conditions.
threshold number 0 仅处理大于此尺寸的资源(单位:字节)
algorithms Algorithms ['gzip', 'brotliCompress'] 使用的压缩算法数组或预定义算法结果
filename string | function [path][base].gz or [path][base]. br (若算法为zstandard则生成[path][base].zst 生成的目标资源文件名规则
deleteOriginalAssets boolean false 是否删除原始资源文件
skipIfLargerOrEqual boolean true 若压缩结果大于或等于原文件时是否跳过压缩
logLevel string info 控制标准输出的信息级别
artifacts function undefined 有时需要将某些内容复制到最终输出,此选项可提供帮助

**API: defineAlgorithm(algorithm, options?)**定义一种带有选项的压缩算法

参数:

  • algorithm算法名称('gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw' | 'zstandard' | 'gz' | 'br' | 'brotli' | 'zstd')或自定义函数
  • options算法的压缩选项

支持的算法:

算法 文件后缀 描述 备注
gzip .gz 标准 gzip 压缩,速度和压缩比平衡良好 支持Node所有版本
brotliCompress .br Brotli 压缩比比 gzip 更好。 支持Node所有版本
deflate .gz Deflate压缩算法 支持Node所有版本
deflateRaw .gz 原始的无标头压缩 支持Node所有版本
zstandard .zst Z标准压缩比,具有出色的速度/传动比平衡性 版本 >= 22.15.0 或 >= 23.8.0
自定义功能 - 自己实现的压缩算法 支持Node所有版本

启用 deleteOriginalAssets 选项删除原始资源文件后,部署时需要确保服务器正确配置以支持压缩文件的访问:

配置服务器识别压缩文件: 服务器需根据客户端请求的 Accept-Encoding 头返回对应的压缩文件(如 .gz.br)。

nginx 配置示例:(关键)

server {
  listen 80;
  root /usr/share/nginx/html;

  location / {
    # 开启静态压缩文件支持
    gzip_static on;        # 自动查找 .gz 文件
    brotli_static on;      # 自动查找 .br 文件(需安装 ngx_brotli 模块)

    # 回退机制:若压缩文件不存在则返回原始文件
    try_files $uri $uri/ @fallback;

    # 缓存策略
    add_header Cache-Control "public, max-age=31536000" always;
    add_header Vary Accept-Encoding; # 根据 Accept-Encoding 返回对应版本
  }

  # fallback 用于 SPA 路由
  location @fallback {
    rewrite ^.*$ /index.html break;
  }
}

✅ 必须确保:

插件生成的 .gz 和 .br 文件与原文件同目录

Nginx 已编译或加载 ngx_brotli 模块(GitHub: google/ngx_brotli)

MIME 类型正确映射(JS/CSS/SVG 等均可被压缩)

4. 第三方依赖分包策略

配置位置

vite.config.tsbuild.rollupOptions.output.manualChunks 中进行拆包。

export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
  return {
    build: {
      // 打包前清空 dist 目录
      emptyOutDir: true,
      // 启用CSS代码分割
      cssCodeSplit: true,
      // 生成sourcemap(生产环境建议设为false)
      sourcemap: false,
      // 资源内联阈值(默认4096,可适当调高)
      assetsInlineLimit: 8192,
      // 指定静态资源目录
      assetsDir: "static",
      // 启用minify(默认esbuild,可改为terser获得更好压缩率)
      minify: "terser",
      // 代码压缩配置
      terserOptions: {
        // 生产环境移除console
        compress: {
          drop_console: true, // 移除 console
          drop_debugger: true,  // 移除 debugger
        },
        format: {
          comments: false,  // 删除注释
        },
      },
      chunkSizeWarningLimit: 2000, // chunk大小警告限制
      rollupOptions: {
        onwarn(warning) {
          // 忽略某些非致命警告(如 eval 使用)
          if (warning.code === 'EVAL') return;
          console.warn(`Rollup warning: ${warning.message}`);
        },
        output: {
          // 静态资源命名规范
          assetFileNames: "assets/[ext]/[name]-[hash].[ext]",
          chunkFileNames: "js/[name]-[hash].js",
          entryFileNames: "js/[name]-[hash].js",
          // 动态分析自动分包
          manualChunks(id) {
            if (id.includes("node_modules")) {
              const match = /[\\/]node_modules[\\/](@?[^\\/]+(?:[\\/][^\\/]+)?)/.exec(id);
              const packageName = match ? match[1] : null;

              // 将较大的库单独打包
              const largeLibs = ['element-plus', 'echarts', 'lodash', 'moment']
              if (packageName && largeLibs.includes(packageName)) {
                return packageName
              }

              // 其他依赖分组打包
              return `vendor-${packageName}`;
            }
          },
        },
      },
    },
  }
})

当启用 assetFileNames: "assets/[ext]/[name]-[hash].[ext]" 时,哈希变化会触发缓存更新。

静态资源命名带 [hash] 可实现长期缓存(max-age=31536000

💡 manualChunksdynamic import()(如 import('./module'))协同工作,Rollup 会优先将异步模块单独打包成 chunk,再通过 manualChunks 对同步依赖进一步拆分。

5. 综合配置与问题分析

集成模板

// vite.config.ts
export default defineConfig(({ mode }: ConfigEnv): UserConfig => { 
  return {
    plugins: setupVitePlugins(),
    build: {
      // 见上文完整 build 配置
    }
  }
})

/* ------------------- 分割线 -------------------- */
// config/plugins/index.ts
import type { PluginOption } from "vite";
import { visualizer } from "rollup-plugin-visualizer";
import { compression, defineAlgorithm } from "vite-plugin-compression2";
import { ViteImageOptimizer } from "vite-plugin-image-optimizer";

/**
 * vite插件
 * @returns PluginOption[]
 */
export const setupVitePlugins = (): PluginOption[] => {
  const isProd = process.env.NODE_ENV === "production";

  const plugins = [
    // ...其它插件配置省略

    // 文件压缩 https://github.com/nonzzz/vite-plugin-compression
    compression({
      algorithms: [
        'gzip',
        defineAlgorithm('brotliCompress')
      ],
    }),
    // 图片优化
    ViteImageOptimizer({
      // 针对不同格式的压缩参数
      png: { quality: 80 },
      jpeg: { quality: 80 },
      jpg: { quality: 80 },
      webp: { quality: 80, lossless: false }, // 转换WebP,非无损模式
      // 可选:排除node_modules或指定小图
      exclude: /node_modules/,
      // 可选:仅处理特定路径
      // include: /public\/images/,
    }),
  ];


  // 打包分析(生产环境可关闭)
  if (isProd) {
    plugins.push(
      // 生成分析报告
      visualizer({
        open: false, // 构建完成后自动打开报告页面
        filename: "stats.html", // 报告文件路径
        gzipSize: true, // 显示 gzip 压缩后的体积
        brotliSize: true, // 显示 brotli 压缩后的体积
      }),
    );
  }

  return plugins;
};

性能收益预估

优化项 平均体积下降 加载时间改善 备注
打包分析 + 移除无用依赖 ~10%-20% ⬆️ 10% 可视化辅助决策
图像压缩(WebP + 质量80) ~30%-60% ⬆️ 显著 尤其适合含图站群
Gzip/Brotli 压缩 ~60%-70% ⬆️ 显著 需服务端支持
依赖分包(vendor 拆分) ~首包减半 ⬆️ 首次加载更快,利于缓存

💡 注:实际效果因项目而异,请结合 visualizer 报告持续迭代。

常见陷阱与规避方法

问题 原因 解决方案
页面白屏 / JS 加载失败 启用了 deleteOriginalAssets: trueNginx 未开启 gzip_static 确保服务器配置支持 .gz/.br 文件解析
SVG 图标变形或丢失 svgo 默认配置过于激进 自定义 svgo 配置保留 fill、stroke 属性
WebP 不兼容旧浏览器 缺少降级机制 使用 <picture> 标签提供 JPEG/PNG 回退
分包过多导致 HTTP 请求增多 manualChunks 拆得太细 控制 vendor 包数量,合并小型库
图片优化插件报错(sharp 安装失败) Node ABI 不匹配或平台不兼容 使用 cross-env SHARP_IGNORE_GLOBAL_LIBVIPS=true npm install sharp 或切换镜像源

✅ 总结

  • 先分析,再优化:每次上线前运行 visualizer 查看 bundle 构成。
  • 渐进式压缩:先启用 Gzip,再逐步加入 BrotliWebP
  • 服务端协同:前端压缩 自动生效,必须配置 Nginx/Apache/Caddy 支持 .gz/.br
  • 缓存策略匹配:静态资源加 [hash],设置 Cache-Control: max-age=31536000
  • 监控真实用户体验:使用 Lighthouse、CrUX 数据跟踪 FCP、LCP 指标变化。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐