Vite 进阶配置:插件开发、按需加载与生产环境优化

Vite 进阶配置:插件开发、按需加载与生产环境优化


目标与场景

  • 插件开发:编写可复用的 Vite 插件(基于 Rollup 插件接口 + Vite 拓展钩子)
  • 按需加载:路由/组件/资源的懒加载与自动导入,提高首屏与交互性能
  • 生产优化:分包策略、预构建、资源提示与分析,稳定降低包体与构建时间

插件架构与钩子

  • 基于 Rollup 插件体系,Vite 额外提供 configureServertransformIndexHtmlhandleHotUpdate
  • 插件编写要点:
    • name 唯一、apply 指定运行阶段('serve' | 'build' | 'client' | 'server'
    • 使用 enforce: 'pre' | 'post' 控制钩子前后顺序

示例:注入构建 banner + 开发阶段虚拟模块

ts 复制代码
// vite-plugin-banner.ts
import type { Plugin } from 'vite'
export default function bannerPlugin(text = 'Built with Vite'): Plugin {
  return {
    name: 'vite-plugin-banner',
    apply: 'build',
    generateBundle(_, bundle) {
      for (const f of Object.values(bundle)) {
        if (f.type === 'chunk') f.code = `/* ${text} */\n` + f.code
      }
    }
  }
}

// vite-plugin-virtual.ts
import type { Plugin } from 'vite'
const ID = 'virtual:env'
export default function virtualEnvPlugin(): Plugin {
  return {
    name: 'vite-plugin-virtual-env',
    resolveId(id) { return id === ID ? id : null },
    load(id) { if (id === ID) return `export const mode = '${process.env.NODE_ENV}'` },
    handleHotUpdate(ctx) {
      if (ctx.file.includes('.env')) ctx.server.ws.send({ type: 'full-reload' })
    }
  }
}

示例:注入 HTML 资源提示

ts 复制代码
// vite-plugin-preload.ts
import type { Plugin } from 'vite'
export function preloadPlugin(): Plugin {
  return {
    name: 'vite-plugin-preload',
    transformIndexHtml(html) {
      const hint = `<link rel="preconnect" href="https://cdn.example.com">`
      return html.replace('</head>', `${hint}</head>`)
    }
  }
}

开发服务器拓展

  • 通过 configureServer 注入中间件,或调用 server.ws.send 实现自定义热更新
ts 复制代码
// vite-plugin-middleware.ts
import type { Plugin } from 'vite'
export function middlewarePlugin(): Plugin {
  return {
    name: 'vite-plugin-middleware',
    apply: 'serve',
    configureServer(server) {
      server.middlewares.use('/health', (_req, res) => { res.statusCode = 200; res.end('ok') })
    }
  }
}

按需加载策略

  • 路由懒加载:() => import('...') 动态导入分离路由包
ts 复制代码
// vue-router
const routes = [ { path: '/', component: () => import('@/pages/Home.vue') } ]
  • 文件式路由与批量懒加载:
ts 复制代码
const pages = import.meta.glob('/src/pages/**/*.vue', { eager: false })
  • 组件按需与自动导入:
ts 复制代码
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default { plugins: [
  AutoImport({ imports: ['vue','vue-router','pinia'] }),
  Components({ resolvers: [ElementPlusResolver()] })
] }
  • 资源懒加载:图片 loading="lazy"、大型组件 defineAsyncComponent、非关键脚本 type=module + async/defer

生产环境优化配置

  • 分包策略与手动切块:
ts 复制代码
// vite.config.ts(Rollup 配置)
import { defineConfig } from 'vite'
export default defineConfig({
  build: {
    sourcemap: true,
    chunkSizeWarningLimit: 800,
    rollupOptions: {
      output: {
        manualChunks: {
          framework: ['vue','vue-router','pinia'],
          ui: ['element-plus'],
          chart: ['echarts']
        }
      }
    }
  }
})
  • 预构建与依赖优化:
ts 复制代码
export default defineConfig({
  optimizeDeps: { include: ['lodash-es','dayjs'], exclude: ['big-lib'] }
})
  • 资源提示与公共路径:
ts 复制代码
export default defineConfig({
  base: 'https://cdn.example.com/',
  build: { assetsInlineLimit: 8 * 1024 }
})
  • CSS 与图片:开启 CSS 代码分割(默认),图片使用 AVIF/WebP 与响应式 srcset
  • Env 与编译:define 注入常量、esbuild 目标(target: 'es2017')减少转译成本

SSR/SSG 与 Streaming(可选)

  • ssr.noExternal 控制外部依赖打包;vite-plugin-ssr 或框架内置(Nuxt/Next)实现 SSR/SSG
  • HTML Streaming:结合框架 Suspense 输出,提高可感知速度

构建分析与检查

  • 可视化:
ts 复制代码
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({ build: { rollupOptions: { plugins: [visualizer({ filename: 'stats.html' })] } } })
  • 插件诊断:vite-plugin-inspect 查看插件链与转换结果
ts 复制代码
import Inspect from 'vite-plugin-inspect'
export default defineConfig({ plugins: [Inspect()] })

Dev 与 Proxy

ts 复制代码
export default defineConfig({
  server: {
    proxy: {
      '/api': { target: 'https://api.example.com', changeOrigin: true, rewrite: p => p.replace(/^\/api/, '') }
    }
  }
})

生产脚本与 CI

  • 使用 CI=true vite build --mode production 固化生产模式;在 CI 缓存 node_modules.vite 目录
  • 结合 pnpm i --frozen-lockfile 与构建日志采集,形成度量闭环

常见坑与修复

  • 预构建冲突:optimizeDeps.include/exclude 精确控制;避免同时引入 CJS/ESM 导致重复
  • 过度分包导致请求过多:为首屏路由保留合并,后续路由按需分割
  • 插件顺序:enforce 配置不当导致转换顺序错误;通过 vite-plugin-inspect 排查
  • CDN 路径与资源 404:base 与静态资源路径需一致;部署端正确配置缓存与回源

实战清单(12 项)

  • 编写通用插件:banner、virtual、preload 与中间件注入
  • 路由/文件式懒加载:import()import.meta.glob
  • 自动导入:unplugin-auto-import 与组件按需 unplugin-vue-components
  • 分包:框架/UI/图表独立与首屏合并;manualChunks
  • 预构建:optimizeDeps 的 include/exclude 管理
  • CDN 与公共路径:baseassetsInlineLimit
  • 图片与字体:现代格式与 font-display: swap
  • Env 与编译:defineesbuild 目标
  • SSR/SSG(可选):ssr.noExternal 与 Streaming
  • 分析:visualizer 与 inspect
  • DevServer 与代理:server.proxy 与中间件
  • CI:锁定依赖、缓存目录与构建日志度量

结果与总结

  • 通过插件与按需策略,首屏包体与交互性能显著改善;生产构建更稳定、分析与度量更清晰
  • 建议以"度量→策略→验证"的闭环长期维护,结合 CDN 与缓存策略持续优化用户体验

高级插件示例

ts 复制代码
import type { Plugin } from 'vite'
export function rewritePlugin(): Plugin {
  return {
    name: 'vite-plugin-rewrite',
    enforce: 'pre',
    transform(code, id) {
      if (id.endsWith('.ts') || id.endsWith('.js')) return code.replace(/@\//g, '/src/')
    }
  }
}

预构建与扫描优化

ts 复制代码
import { defineConfig } from 'vite'
export default defineConfig({
  optimizeDeps: {
    entries: ['src/main.ts'],
    include: ['lodash-es','dayjs'],
    esbuildOptions: { target: 'es2017' }
  }
})

模块预加载

ts 复制代码
export default defineConfig({ build: { modulePreload: { polyfill: true } } })

构建度量

bash 复制代码
CI=true vite build --mode production --profile

文件系统与 HMR

ts 复制代码
export default defineConfig({ server: { fs: { strict: true }, hmr: { overlay: true } } })

更多问题与定位

  • 插件覆盖与顺序:调整 enforce 并使用 vite-plugin-inspect 观察链路
  • CJS 与 ESM 混用:通过 optimizeDeps.exclude 排除并统一使用 ESM 入口
  • 资源哈希与缓存:校验 build.assetsInlineLimit 与输出哈希策略

度量目标

  • 首屏路由包体下降 30% 以上
  • 构建时间下降 40% 以上,二次构建下降 70% 以上
  • 预构建扫描耗时下降 50% 以上
相关推荐
侠客行03176 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪6 小时前
深入浅出LangChain4J
java·langchain·llm
子兮曰6 小时前
OpenClaw入门:从零开始搭建你的私有化AI助手
前端·架构·github
吴仰晖6 小时前
使用github copliot chat的源码学习之Chromium Compositor
前端
1024小神6 小时前
github发布pages的几种状态记录
前端
灰子学技术7 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
老毛肚7 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎8 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
二十雨辰8 小时前
[python]-AI大模型
开发语言·人工智能·python
不像程序员的程序媛8 小时前
Nginx日志切分
服务器·前端·nginx