Files
zhuangang/topics/frontend/vite/01-dev-build-config.md
T

4.6 KiB
Raw Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
frontend.vite.dev-build-config Vite 8:开发服务器原理、Rolldown 构建、环境变量、代理(含 SSE)、插件和拆包 lesson frontend
vite
Vite
Rolldown
ESM
HMR
环境变量
代理
插件
虚拟模块
拆包
intermediate
frontend.typescript.type-system
2.5
frontend.webpack.build-optimization
frontend.vite.scenario.webpack-to-vite
frontend.vite.interview
7
1

Vite 8:开发服务器原理、Rolldown 构建、环境变量、代理(含 SSE)、插件和拆包

基于 Vite 8.3 编写并实际构建验证。Vite 8 的生产构建改为基于 Rust 编写的 Rolldown(Vite 7 及以前:开发时用 esbuild 预构建依赖,生产用 Rollup 打包);build.rollupOptions 改名为 build.rolldownOptions(旧名仍可用但已标记弃用),拆包推荐 output.codeSplitting。以官方文档为准:https://vite.dev/guide/ 、https://rolldown.rs/ 。

解释说明

为什么开发时快

  • 开发:不打包。浏览器原生支持 ES 模块(<script type="module">),请求哪个文件,Vite 就即时转换哪个文件(TS → JS、.vue → JS)。第三方依赖会预构建(把 CommonJS 转成 ESM、把很多小文件合成一个),并强缓存。
  • 热更新(HMR):文件修改后只让对应模块失效并重新请求,速度与项目大小基本无关。
  • 生产构建:仍然要打包(减少请求数、tree shaking、压缩、拆包)。

常用配置

需求 配置
路径别名 resolve.alias
环境变量 .env、.env.[mode];浏览器代码只能读取 VITE_ 前缀的变量(import.meta.env.VITE_XXX);配置文件中用 loadEnv
开发代理 server.proxy,解决开发时的跨域,支持 rewrite
插件 Rollup / Rolldown 兼容的插件钩子(resolveId、load、transform)+ Vite 专有钩子(configureServer、transformIndexHtml、handleHotUpdate)
拆包 Vite 8:build.rolldownOptions.output.codeSplitting.groups;动态 import() 自动拆包
兼容旧浏览器 @vitejs/plugin-legacy

Monorepo

pnpm workspace + Vite 是常见组合:多个应用共用组件库和工具包,workspace:* 引用本地包。

案例

AI 客服前端项目:开发时 /api 代理到本地 FastAPI,/stream 代理 SSE 流式接口;构建时注入版本号(便于排查线上问题对应哪个版本);Vue 运行时单独拆包,长期缓存。

代码示例

完整项目:examples/vite-config。已验证:tsc 类型检查通过;vite build 输出独立的 vendor-vue 和按需加载的 heavy chunk;开发服务器代理普通接口(/api/hello → 后端 /hello)和 SSE(每 0.5 秒一条,逐条到达,没有被缓冲)。

自定义插件——虚拟模块(vite.config.ts):

function buildInfo(version: string): Plugin {
  const id = "virtual:build-info";
  const resolved = "\0" + id;
  return {
    name: "build-info",
    resolveId(source) { return source === id ? resolved : null; },
    load(moduleId) {
      if (moduleId !== resolved) return null;
      return `export default ${JSON.stringify({ version, builtAt: new Date().toISOString() })};`;
    },
  };
}

代理和拆包:

server: {
  proxy: {
    "/api": { target: apiTarget, changeOrigin: true, rewrite: (p) => p.replace(/^\/api/, "") },
    "/stream": { target: apiTarget, changeOrigin: true },
  },
},
build: {
  rolldownOptions: {
    output: { codeSplitting: { groups: [{ name: "vendor-vue", test: /node_modules[\\/]@?vue/ }] } },
  },
},

构建输出:

dist/assets/heavy-xxxx.js        0.08 kB
dist/assets/index-xxxx.js        2.54 kB
dist/assets/vendor-vue-xxxx.js  60.80 kB

易错点

  • 把密钥放到 VITE_ 前缀的变量中:会被打包进浏览器代码,任何人都能看到。
  • 以为开发代理在生产环境也生效:生产环境需要 Nginx 或网关转发。
  • 开发正常、构建后报错:开发不打包,CommonJS 依赖、动态路径、大小写敏感的路径在构建时才暴露问题,提交前要跑一次 vite build。
  • 照搬旧文章的 rollupOptions.output.manualChunks:Vite 8 中仍可用但已弃用,新项目使用 codeSplitting。
  • 拆包拆得太碎:请求数增加,反而变慢。

面试常问

  • Vite 为什么比 webpack 开发时快?生产构建为什么还要打包?
  • Vite 的环境变量是如何区分浏览器可见和不可见的?
  • 写过 Vite 插件吗?说说钩子。