web-tooling-vite

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Vite Patterns

Vite 模式

Quick Guide: Vite serves unbundled ES modules in development and bundles for production, so most configuration decisions apply to only one of those halves. The version decides the API: Vite 8 bundles with Rolldown (
build.rolldownOptions
,
codeSplitting.groups
,
resolve.tsconfigPaths
), Vite 7 with Rollup (
build.rollupOptions
,
manualChunks
, aliases mirrored into
tsconfig.json
by hand). Sass runs on the modern API from Vite 6 onwards, and the legacy API is gone from Vite 7.
Detailed Resources:
  • examples/core.md — full configs for aliases, chunk splitting, environment-specific builds, module preload, Sass, the Environment API and the dev proxy
  • reference.md — dev/production settings, build-target table, per-version notes and the Vite 8 migration checklist

快速指南: Vite 在开发环境中提供未打包的 ES modules,并在生产环境中进行打包,因此大多数配置决策只适用于这两个阶段中的一个。版本决定所使用的 API:Vite 8 使用 Rolldown 打包(
build.rolldownOptions
、
codeSplitting.groups
、
resolve.tsconfigPaths
),Vite 7 使用 Rollup(
build.rollupOptions
、
manualChunks
,别名需手动同步到
tsconfig.json
)。从 Vite 6 开始,Sass 使用 modern API;Vite 7 已移除 legacy API。
详细资源:
  • examples/core.md——别名、chunk 拆分、分环境构建、module preload、Sass、Environment API 和开发代理的完整配置
  • reference.md——开发/生产设置、build target 对照表、各版本注意事项和 Vite 8 迁移检查清单

Which path applies

应采用哪条路径

Read the
vite
version in
package.json
before writing config; the two branches share names and not behaviour.
  • Vite 8 (Rolldown) —
    build.rolldownOptions
    ,
    codeSplitting.groups
    for chunking,
    resolve.tsconfigPaths
    instead of hand-mirrored aliases.
    build.rollupOptions
    still resolves as a deprecated alias, which is why a wrong-version config looks like it works.
  • Vite 7 (Rollup) —
    build.rollupOptions
    with
    manualChunks
    , and aliases declared in both
    vite.config.ts
    and
    tsconfig.json
    .

<critical_requirements>
在编写配置前,先查看
package.json
中的
vite
版本;这两个分支虽然名称相同,但行为不同。
  • Vite 8(Rolldown)——使用
    build.rolldownOptions
    ,通过
    codeSplitting.groups
    进行 chunk 拆分,并使用
    resolve.tsconfigPaths
    ,而不是手动同步别名。
    build.rollupOptions
    仍可解析,但它是 deprecated alias,因此错误版本的配置看起来也能正常工作。
  • Vite 7(Rollup)——使用带有
    manualChunks
    的
    build.rollupOptions
    ,并在
    vite.config.ts
    和
    tsconfig.json
    中同时声明别名。

<critical_requirements>

Before writing Vite config

编写 Vite 配置前

Declare path aliases in one place per version —
resolve.tsconfigPaths
on Vite 8, or the same set in both
vite.config.ts
and
tsconfig.json
on Vite 7. A pair that has drifted resolves in one tool and not the other, so the build and the editor disagree about the same import.
Match the bundler options to the version. On Vite 8 that is
build.rolldownOptions
with
codeSplitting.groups
; object-form
manualChunks
is removed there and silently produces no grouping.
Split config by mode rather than shipping one build.
defineConfig(({ mode }) => …)
with
loadEnv
keeps minification and sourcemaps out of development and out of the production bundle respectively.
Reach for
build.modulePreload.polyfill
, the current spelling —
build.polyfillModulePreload
is deprecated and offers no control over which dependencies are preloaded.
</critical_requirements>

Auto-detection: Vite, vite.config.ts, vite.config.js, defineConfig, loadEnv, manualChunks, advancedChunks, codeSplitting.groups, rolldownOptions, rollupOptions, modulePreload, resolve.alias, resolve.tsconfigPaths, build.target, baseline-widely-available, VITE_
Applies to:
  • Configuring a Vite build for a frontend application
  • Path aliases, and keeping them resolvable by both the bundler and the type checker
  • Vendor chunk splitting for cacheable production output
  • Per-mode builds (development, staging, production) with
    loadEnv
    and
    define
  • Migrating a config from Vite 7 to Vite 8
  • Module preload, build targets and Sass preprocessing
Handled elsewhere:
  • Compiler options and type-checking behaviour, beyond the
    paths
    entry an alias needs
  • Lint and format configuration
  • Application code that runs in the browser — this is build-time configuration only
  • Build orchestration by an SSR meta-framework, which owns its own config file
  • Pipeline and deployment concerns around the build command

<philosophy>
Development and production run different machinery. The dev server transforms modules on demand and ships them unbundled, so nothing there is tree-shaken, minified or chunked; the production build is a bundler run with all three. Every defect that survives to release lives in that gap — a stylesheet dropped by tree-shaking, a chunk that only exists in production, an env var that was present locally.
So the useful question about any option is which half it governs, and whether the other half will ever exercise it.
</philosophy>
<decision_framework>
每个版本只在一处声明 path aliases——Vite 8 使用
resolve.tsconfigPaths
,Vite 7 则在
vite.config.ts
和
tsconfig.json
中声明同一组别名。如果两处配置发生偏差,解析结果就会在工具之间不一致,导致 build 和 editor 对同一 import 的解析结论不同。
使 bundler options 与版本匹配。 Vite 8 使用带有
codeSplitting.groups
的
build.rolldownOptions
;对象形式的
manualChunks
在该版本中已被移除,并且会悄无声息地导致不进行任何分组。
按 mode 拆分配置,而不是始终交付同一种 build。 使用带有
loadEnv
的
defineConfig(({ mode }) => …)
,可以确保 development 不进行压缩,并确保 production bundle 不包含 sourcemap。
使用
build.modulePreload.polyfill
,这是当前拼写方式;
build.polyfillModulePreload
已弃用,而且无法控制要预加载哪些依赖项。
</critical_requirements>

自动检测: Vite、vite.config.ts、vite.config.js、defineConfig、loadEnv、manualChunks、advancedChunks、codeSplitting.groups、rolldownOptions、rollupOptions、modulePreload、resolve.alias、resolve.tsconfigPaths、build.target、baseline-widely-available、VITE_
适用于:
  • 为前端应用配置 Vite build
  • 路径别名,以及确保 bundler 和类型检查器都能解析这些别名
  • 拆分 vendor chunk,以生成可缓存的生产环境产物
  • 使用
    loadEnv
    和
    define
    按 mode 构建(development、staging、production)
  • 将配置从 Vite 7 迁移到 Vite 8
  • Module preload、build target 和 Sass 预处理
不在此处处理:
  • 编译器选项和类型检查行为,但不包括 alias 所需的
    paths
    条目
  • Lint 和格式化配置
  • 在浏览器中运行的应用代码——本文仅涉及构建时配置
  • SSR meta-framework 执行的构建编排,因为它拥有自己的配置文件
  • 围绕 build command 的 pipeline 和 deployment 事项

<philosophy>
开发和生产运行着不同机制。dev server 按需转换 modules,并以未打包的形式提供,因此其中不会进行 tree-shaking、压缩或 chunk 拆分;production build 则由 bundler 执行这三项。所有遗留到发布阶段的缺陷都源于这种差异——被 tree-shaking 丢弃的 stylesheet、仅在 production 中存在的 chunk,或仅在本地存在的环境变量。
因此,评估任何选项时,真正有用的问题是:它控制哪一半,以及另一半是否会在任何时候用到它。
</philosophy>
<decision_framework>

Chunk splitting

Chunk 拆分

Vite 8 (Rolldown)?
├─ Separating vendors by package → codeSplitting.groups with a test regex ✓
└─ Constraining by size or share count → codeSplitting maxSize / minSize / minShareCount

Vite 7 (Rollup)?
├─ Separating vendors by package → manualChunks, object form ✓
└─ Logic per module id → manualChunks, function form
advancedChunks
is the same feature under its rolldown-vite name; it became
codeSplitting
in Vite 8.
Vite 8 (Rolldown)?
├─ 按依赖包分离 vendor → 使用 codeSplitting.groups 和 test regex ✓
└─ 按大小或共享数量限制 → codeSplitting maxSize / minSize / minShareCount

Vite 7 (Rollup)?
├─ 按依赖包分离 vendor → manualChunks,对象形式 ✓
└─ 按 module id 划分逻辑 → manualChunks,函数形式
advancedChunks
是同一功能在 rolldown-vite 下的名称;在 Vite 8 中,它更名为
codeSplitting
。

Build target

Build target

Are there browsers below the default floor to support?
├─ YES → @vitejs/plugin-legacy alongside the modern build
└─ NO  → Smallest possible output?
    ├─ YES → 'esnext'
    └─ NO  → 'baseline-widely-available' (the default) ✓, or an explicit list
              such as ['chrome111', 'safari16.4'] when a contract names versions
</decision_framework>

<patterns>
需要支持低于默认下限的浏览器吗?
├─ 是 → 在 modern build 旁使用 @vitejs/plugin-legacy
└─ 否  → 需要尽可能小的输出?
    ├─ 是 → 'esnext'
    └─ 否 → 'baseline-widely-available'(默认值)✓,或在合同时指定版本时
              显式列出,例如 ['chrome111', 'safari16.4']
</decision_framework>

<patterns>

Core patterns

核心模式

Pattern 1: Path Aliases

模式 1:路径别名

One alias set, resolvable by the bundler and the type checker alike.
typescript
// Vite 8 — read straight from tsconfig.json
resolve: { tsconfigPaths: true },

// Vite 7 — declared here and mirrored in tsconfig.json "paths"
resolve: {
  alias: { "@": path.resolve(__dirname, "./src") },
},
resolve.tsconfigPaths
is off by default because resolving through tsconfig costs startup time; turn it on only where the project uses
paths
. It covers only what
tsconfig.json
declares, so an alias with no
paths
entry — a package outside the project, a stub swapped in for a build — still needs
resolve.alias
beside it.
Full code: examples/core.md

使用同一组别名,并确保 bundler 和类型检查器都能解析。
typescript
// Vite 8 — read straight from tsconfig.json
resolve: { tsconfigPaths: true },

// Vite 7 — declared here and mirrored in tsconfig.json "paths"
resolve: {
  alias: { "@": path.resolve(__dirname, "./src") },
},
resolve.tsconfigPaths
默认关闭,因为通过 tsconfig 解析会增加启动时间;仅在项目使用
paths
时开启。它只涵盖
tsconfig.json
中声明的内容,因此没有对应
paths
条目的 alias——例如项目之外的 package,或为构建替换的 stub——仍需同时配置
resolve.alias
。
完整代码:examples/core.md

Pattern 2: Vendor Chunk Splitting

模式 2:Vendor chunk 拆分

Dependencies change on a slower cadence than application code, so a separate chunk stays in the browser cache across deploys.
typescript
// Vite 8
build: {
  rolldownOptions: {
    output: {
      codeSplitting: {
        groups: [{ name: "vendor", test: /[\\/]node_modules[\\/]/ }],
      },
    },
  },
},
Narrow the
test
per group where different dependencies move at different rates; one chunk holding everything is invalidated by any upgrade.
Full code, including the Vite 7 form: examples/core.md

依赖项的变更频率低于应用代码,因此单独分出的 chunk 能在多次部署之间保留于浏览器缓存中。
typescript
// Vite 8
build: {
  rolldownOptions: {
    output: {
      codeSplitting: {
        groups: [{ name: "vendor", test: /[\\/]node_modules[\\/]/ }],
      },
    },
  },
},
当不同依赖项的更新频率不同时,应为每个 group 收窄
test
;将所有内容放在一个 chunk 中,会因任意一次升级而失效。
包含 Vite 7 形式在内的完整代码:examples/core.md

Pattern 3: Per-Mode Builds

模式 3:分 mode 构建

defineConfig
takes a function, so the config can differ per
--mode
.
typescript
export default defineConfig(({ mode }) => ({
  build: {
    sourcemap: mode !== "production",
    minify: mode === "production",
  },
}));
.env
file selection follows
--mode
, not
NODE_ENV
.
loadEnv(mode, cwd, "")
returns every variable, including ones with no client prefix, so treat its result as server-side values rather than something to feed into
define
.
Full code: examples/core.md

defineConfig
接受一个函数,因此配置可以随
--mode
变化。
typescript
export default defineConfig(({ mode }) => ({
  build: {
    sourcemap: mode !== "production",
    minify: mode === "production",
  },
}));
.env
文件的选择依据是
--mode
,而不是
NODE_ENV
。
loadEnv(mode, cwd, "")
会返回所有变量,包括没有客户端前缀的变量,因此应将其结果视为服务端值,而不是传给
define
的内容。
完整代码:examples/core.md

Pattern 4: Module Preload

模式 4:Module preload

typescript
build: {
  modulePreload: {
    polyfill: false,
    resolveDependencies: (filename, deps) =>
      deps.filter((dep) => !dep.includes("rarely-used-vendor")),
  },
},
Dropping the polyfill is safe only where the target floor supports module preload natively;
resolveDependencies
is how a large chunk is kept out of the initial preload list.
Full code: examples/core.md

typescript
build: {
  modulePreload: {
    polyfill: false,
    resolveDependencies: (filename, deps) =>
      deps.filter((dep) => !dep.includes("rarely-used-vendor")),
  },
},
只有当目标浏览器下限原生支持 module preload 时,移除 polyfill 才是安全的;
resolveDependencies
可以将大型 chunk 排除在初始 preload 列表之外。
完整代码:examples/core.md

Pattern 5: Sass

模式 5:Sass

typescript
css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@use "./src/styles/variables" as *;`,
    },
  },
},
The modern API is the default from Vite 6 and the only one from Vite 7, so a config still passing
api: "legacy"
fails rather than falling back. Migrating means
@import
becoming
@use
/
@forward
with namespaced access.

typescript
css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@use "./src/styles/variables" as *;`,
    },
  },
},
modern API 从 Vite 6 开始就是默认选项,并且是 Vite 7 的唯一选项,因此仍传入
api: "legacy"
的配置会直接失败,而不会回退。迁移意味着将
@import
改为
@use
/
@forward
,并使用命名空间方式访问。

Pattern 6: Dev Server Proxy

模式 6:Dev server 代理

typescript
server: {
  proxy: {
    "/api": { target: "http://localhost:8000", changeOrigin: true },
  },
},
Proxying keeps development same-origin, so no CORS configuration is needed for a backend that will be same-origin in production anyway.

typescript
server: {
  proxy: {
    "/api": { target: "http://localhost:8000", changeOrigin: true },
  },
},
使用代理可以让开发环境保持同源,因此对于生产环境中也将同源的后端,无需配置 CORS。

Pattern 7: Environment API

模式 7:Environment API

For framework authors and multi-runtime builds; still RC as of Vite 8, and an ordinary single-client app needs none of it.
typescript
environments: {
  client: { build: { outDir: "dist/client" } },
  ssr: { build: { outDir: "dist/server", ssr: true } },
},
Full code: examples/core.md
</patterns>
<red_flags>
面向 framework 作者和多 runtime 构建;截至 Vite 8,该 API 仍处于 RC 阶段,普通单客户端应用不需要使用它。
typescript
environments: {
  client: { build: { outDir: "dist/client" } },
  ssr: { build: { outDir: "dist/server", ssr: true } },
},
完整代码:examples/core.md
</patterns>
<red_flags>

Red flags

危险信号

Breaks at runtime:
  • Object-form
    manualChunks
    on Vite 8 — removed, so the grouping silently does not happen and the vendor chunk you expected is not in
    dist
  • splitVendorChunkPlugin
    , removed in Vite 7 — the import throws before the build starts
  • target: 'modules'
    , removed in Vite 7 — replaced by
    'baseline-widely-available'
  • Sass
    api: "legacy"
    on Vite 7 or later — the legacy API is gone rather than deprecated
  • An alias present in
    vite.config.ts
    and missing from
    tsconfig.json
    , or the reverse — the build succeeds and the type check fails, or the editor resolves an import the bundler cannot
Surprising behaviour:
  • build.rollupOptions
    still resolves on Vite 8 as a deprecated alias, so a Vite 7 config appears to work until a Rolldown-only option is needed
  • resolve.tsconfigPaths
    is off by default; enabling it is a deliberate step, not a fallback
  • Rolldown emits an extra
    runtime.js
    chunk beside the code-split groups
  • loadEnv(mode, cwd, "")
    loads every variable in the file, prefix or not — inlining one of those through
    define
    publishes it in the bundle
  • build.commonjsOptions
    is a no-op on Vite 8, since Rolldown handles CommonJS natively
  • The
    esbuild
    option auto-converts to
    oxc
    on Vite 8, quietly dropping the options Oxc has no equivalent for
  • Sourcemaps left on in production ship readable source;
    'hidden'
    keeps them for error reporting without linking them from the bundle
</red_flags>
会导致运行时故障:
  • Vite 8 中的对象形式
    manualChunks
    ——该形式已被移除,因此分组会悄无声息地不发生,预期中的 vendor chunk 不会出现在
    dist
    中
  • splitVendorChunkPlugin
    ,Vite 7 中已移除——import 会在 build 开始前抛出错误
  • target: 'modules'
    ,Vite 7 中已移除——已由
    'baseline-widely-available'
    取代
  • Vite 7 及更高版本中的 Sass
    api: "legacy"
    ——legacy API 已被彻底移除,而不只是弃用
  • alias 存在于
    vite.config.ts
    中却缺失于
    tsconfig.json
    ,或情况相反——build 会成功,但类型检查失败;或者 editor 能解析某个 import,而 bundler 无法解析
令人意外的行为:
  • build.rollupOptions
    在 Vite 8 中仍可解析,但它是 deprecated alias,因此 Vite 7 配置会一直看似正常,直到需要使用仅 Rolldown 支持的选项
  • resolve.tsconfigPaths
    默认关闭;启用它是一个明确步骤,而不是 fallback
  • Rolldown 会在 code-split groups 旁额外生成一个
    runtime.js
    chunk
  • loadEnv(mode, cwd, "")
    会加载文件中的所有变量,无论是否具有前缀——通过
    define
    内联其中某个变量会将其发布到 bundle 中
  • build.commonjsOptions
    在 Vite 8 中不起作用,因为 Rolldown 原生处理 CommonJS
  • Vite 8 中的
    esbuild
    选项会自动转换为
    oxc
    ,并悄悄丢弃 Oxc 没有对应实现的选项
  • 在 production 中保留 sourcemap 会发布可读的源代码;
    'hidden'
    会保留 sourcemap 以供错误报告使用,但不会从 bundle 中链接到它们
</red_flags>