Vite进阶用法详解

一、为什么 Vite 的性能思路不一样?

传统工具(Webpack)的做法是:​开发时也打包​。不管项目大小,启动时先把所有模块打包成一个 bundle,启动慢、改一行代码也要重新打包。

Vite 的核心理念是:开发和构建是两个不同的问题,用不同的工具解决。

​ Rolldown 还有一些兼容性问题,故暂时不讲 Rolldown 。

开发阶段 生产构建
工具 浏览器原生 ESM + esbuild Rollup(可插拔,生态成熟)
做了什么 不打包,浏览器直接 import 单个文件,按需请求编译 全量打包 + Tree Shaking + 压缩
速度 启动 < 1s,HMR < 50ms 比 Webpack 快 2-5x
改一行代码 只编译那一个文件 无影响

二、源码层优化(开发体验)

2.1 依赖预构建

Vite 启动时会把 node_modules 里的依赖​提前转成 ESM 并合并​,减少浏览器请求数。

TypeScript 复制代码
// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    include: [  // 默认情况下,不在 node_modules 中链接的包不会预构建
      'lodash-es',           // 我们也可以强制预构建
      'dayjs',
      'echarts',
      '@ant-design/icons-vue'
    ],
    exclude: [
      'vue',                 // 已知 ESM 的库不需要再处理
      'pinia',
      'vue-router'
    ]
  }
})

2.2 持久化缓存

Vite 会把预构建产物和编译缓存写入 node_modules/.vite/,第二次启动直接读缓存。

2.3 多线程编译(esbuild / SWC)

Vite 开发模式用 esbuild 做 transform(TS → JS、JSX 转译等),它是 Go 写的,比 Babel 快 20 倍(官方说的,不是我说的)。

TypeScript 复制代码
// 默认已启用,但可以调优
export default defineConfig({
  esbuild: {
    target: 'es2020',
    minifyWhitespace: true,  // 开发阶段也可压缩空白
    treeShaking: true,
    pure: ViteEnv.VITE_DROP_CONSOLE ? ["console.log", "debugger"] : [] // 打包时是否删除 console debugger
  }
})

可以用 SWC 替代 Babel 优化性能:

TypeScript 复制代码
// 安装 @vitejs/plugin-react-swc
import react from '@vitejs/plugin-react-swc'

export default defineConfig({
  plugins: [react()]  // 默认用 SWC 而不是 Babel
})

三、构建层优化(打包速度与质量)

3.1 chunk 分割 ------ 把「大礼包」拆成「小零食」

如果不做分割,所有代码打成一个大包(比如 5MB),用户打开页面要下载 5MB 才能渲染。

TypeScript 复制代码
export default defineConfig({
  build: {
    outDir: "dist",
    sourcemap: false, // 不生成 sourcemap 减少包体积
    reportCompressedSize: false, // 不计算 gzip 后大小,加快打包速度
    chunkSizeWarningLimit: 2000, // 默认 500KB,提升到 2000KB 减少警告
    rollupOptions: {
      output: {
        manualChunks: {
          // Vue 全家桶一个 chunk
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          // UI 组件库单独拆
          'element-plus': ['element-plus'],
          // 图表库单独拆(很大且不是所有页面都用)
          'echarts': ['echarts', 'vue-echarts'],
          // 工具库
          'utils': ['lodash-es', 'dayjs', 'axios']
        }
      }
    }
  }
})

效果对比:

Plain 复制代码
不分割:  bundle.js                  = 3.2MB
分割后:  vue-vendor.js  (180KB)   ← 所有页面共享,缓存
         element-plus.js (1.1MB)  ← 用了表单页才下载
         echarts.js (980KB)       ← 只有报表页才下载
         index.js (40KB)          ← 首页只需这个

3.2 生产方式改用 esbuild 压缩

TypeScript 复制代码
export default defineConfig({
  build: {
    minify: 'esbuild',            // 现在默认是 'esbuild',比以前默认 terser 快很多
    target: 'es2015',             // 目标浏览器
    cssMinify: 'esbuild',         // CSS 也用 esbuild 压缩
  }
})

3.3 移除调试代码

TypeScript 复制代码
export default defineConfig({
  build: {
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,       // 移除 console.log
        drop_debugger: true
      }
    }
  }
})

注:线上保留 console.warn / console.error 是有争议的,通常是保留错误日志(方便监控),去掉调试日志。

esbuild / terser 都是全量删除所有 console.*。 想精细控制(只删 log 保留 error)需要自己写 Vite 插件。

四、产物层优化(加载性能)

4.1 路由级懒加载 ------ 首屏只加载需要的代码

TypeScript 复制代码
// router/index.ts
const routes = [
  {
    path: '/',
    component: () => import('@/views/Home.vue')       // 首页
  },
  {
    path: '/orders',
    component: () => import('@/views/OrderList.vue')  // 订单管理页
  },
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue')  // 数据看板页
  }
]

Vite 遇到 () => import(...) 会自动生成单独的 chunk。用户访问首页只下载首页的 chunk,切换到订单页才下载订单页的 chunk。

4.2 组件级懒加载 ------ 非关键组件按需加载

Plain 复制代码
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

// 这个组件很大(富文本编辑器、图表等),只在需要时加载
const RichEditor = defineAsyncComponent(() =>
  import('@/components/RichEditor.vue')
)
</script>

<template>
  <RichEditor v-if="showEditor" />
</template>

4.3 Brotli / Gzip 压缩

TypeScript 复制代码
// 安装 vite-plugin-compression
import compression from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    compression({
      algorithm: 'brotliCompress',   // Brotli 比 Gzip 多压缩 15-20%
      threshold: 1024,               // 只压缩 >1KB 的文件
      deleteOriginFile: false,       // 保留原始文件作为 fallback
    })
  ]
})

Nginx 配置自动选择最优压缩:

Nginx 复制代码
# 客户端支持 Brotli 就用 .br,不支持就用 .gz
location /assets {
  gzip_static on;
  brotli_static on;
  expires 1y;
  add_header Cache-Control "public, immutable";
}

五、部署层优化(网络性能)

5.1 CDN + 缓存策略

TypeScript 复制代码
// vite.config.ts 构建时 external 大库
export default defineConfig({
  build: {
    rollupOptions: {
      external: ['vue', 'element-plus'],  // 不打入 bundle
      output: {
        globals: {
          vue: 'Vue',
          'element-plus': 'ElementPlus'
        }
      }
    }
  }
})
HTML 复制代码
<!-- index.html 用 CDN 引入 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3.4/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/element-plus"></script>

5.2 图片优化

TypeScript 复制代码
// vite-plugin-imagemin 自动压缩图片
import imagemin from 'vite-plugin-imagemin'

export default defineConfig({
  plugins: [
    imagemin({
      gifsicle: { optimizationLevel: 7 },
      optipng: { optimizationLevel: 7 },
      mozjpeg: { quality: 80 },
      pngquant: { quality: [0.8, 0.9] },
      webp: { quality: 80 }         // 生成 WebP 格式
    })
  ]
})

配合 HTML 原生懒加载:

Plain 复制代码
<img loading="lazy" src="/images/product.jpg" alt="商品图片" />

5.3 关键资源预加载

TypeScript 复制代码
// vite.config.ts 自动插入 preload 标签
export default defineConfig({
  plugins: [
    // Vite 默认会为入口 chunk 和 CSS 生成 preload
    // 还可以手动预加载关键资源
  ]
})
HTML 复制代码
<!-- 构建产物自动生成类似这样的 preload -->
<link rel="modulepreload" href="/assets/index.abc123.js">
<link rel="preload" href="/assets/font.woff2" as="font" crossorigin>
<link rel="prefetch" href="/assets/orders.chunk.js">  <!-- 下一页预加载 -->

5.4 RUM 监控 ------ 用真实数据驱动优化

一般来说,我们上线前不会"凭感觉"优化,而是先埋点,用真实用户数据指导方向:

TypeScript 复制代码
// 采集 Web Vitals 指标
import { onLCP, onFID, onCLS, onFCP, onTTFB } from 'web-vitals'

onLCP(metric => reportMetric('LCP', metric.value))   // 最大内容绘制
onFID(metric => reportMetric('FID', metric.value))   // 首次输入延迟
onCLS(metric => reportMetric('CLS', metric.value))   // 布局偏移
onFCP(metric => reportMetric('FCP', metric.value))   // 首次内容绘制

核心原则:优化什么,由数据决定,不是由感觉决定。

六、其他配置

6.1 路径别名

TypeScript 复制代码
resolve: {
  alias: {
    "@": resolve(__dirname, "./src")
  }
}
  • @./src:这样可以在代码里写 import X from '@/components/xxx' 而不是 ../../../components/xxx

6.2 SCSS 全局变量注入

TypeScript 复制代码
css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@import "@/styles/var.scss";`
    }
  }
}

作用: 每个 SCSS 文件编译时,自动在最前面插入 @import "@/styles/var.scss",这样所有组件都能直接使用其中的变量($primary-color 等),​不需要每个文件手动 import​。

这里的 @import 是 SCSS 的,不是 CSS 的,注意区分。

6.3 开发服务器配置

TypeScript 复制代码
server: {
  allowedHosts: true, // 允许任何域名访问(谨慎配置)
  hmr: true, // 热更新
  host: '0.0.0.0', // 监听所有网卡
  port: 8080,
  open: true, // 项目启动直接开启浏览器页面
  cors: true, // 允许跨域
  // proxy: xxx // 代理(开发环境一般反向代理)
}

6.4 插件

TypeScript 复制代码
plugins: xxx, // 插件
相关推荐
洛卡卡了1 小时前
Claude Code rules 要怎么用,团队协作时如何统一代码规范呢?
面试·agent·claude
狂炫冰美式2 小时前
人均配了AI, 为什么公司还是没变快? 🤔 本质还是分布式系统问题
前端·后端·架构
铁皮饭盒3 小时前
Next.js 风格路由内置?Bun FileSystemRouter 凭啥这么香
javascript
乘风gg3 小时前
多 Agent 不是万能的!搞懂这 5 个原则,少走 1 年弯路!
前端·agent·ai编程
猩猩程序员3 小时前
Vercel 推出 Agent 框架 Eve:让 AI Agent 像写 Web 应用一样简单
前端
小林ixn4 小时前
别再背八股了!从 5 个真实场景彻底搞懂 JavaScript 的 this
javascript
爱读源码的大都督4 小时前
Claude Code源码分析(三):为什么系统提示词中需要有tools呢?
前端·人工智能·后端
爱勇宝4 小时前
Claude Code 被曝暗藏“隐形检测”代码:封代理不是最可怕的,可怕的是你根本不知道它在干什么
前端·后端·程序员