乐于分享
好东西不私藏

还在死磕官方文档吗,一文让你吃透Vite

还在死磕官方文档吗,一文让你吃透Vite

Vite下一代构建工具之基础配置

一、vite.config.ts基础配置

vite.config.ts是 Vite 项目的核心配置文件,用于自定义开发服务器行为、构建流程、插件集成等关键功能。它基于原生 Es Modules 设计,无需打包即可快速启动开发服务器,并通过简洁的配置实现高效构建。

在了解基础配置之前,我们先来看一个函数defineConfig,它有什么作用呢?

1.1、defineConfig

defineConfig 是 Vite 提供的类型安全辅助函数,用于包裹项目配置对象,核心作用是“提供精准的TypeScript类型提示和校验”,避免配置项拼写错误或类型不匹配问题,它不是必须使用(直接导出普通对象也可运行),但能显著提升配置的可靠性和开发体验。以下从本质、关键特性及最佳实战三方面详解:

1、本质与核心价值

  • 类型安全的配置声明:

    • 问题背景:Vite 配置项繁多(如 server、build、resolve 等),手动编写易拼写错误或遗漏必填项。
    • 解决方案:defineConfig 通过 TypeScript 泛型约束配置结构,在编辑器中实时校验配置合法性。 示例:若误写 serer(多字母 e),TypeScript 会直接报错,而非等到运行时失败。
  • 无需 JSDoc 注解的智能提示:

    • 传统方式需添加 /** @type {import('vite').UserConfig} */ 注释才能获得类型提示。
    • 使用 defineConfig 后,编辑器自动识别配置类型,省去冗余注释,代码更简洁。

2、关键特性详解

2.1、类型安全的配置对象
import { defineConfig } from'vite';

exportdefault defineConfig({
server: {
port3000,  // ✅ 类型校验:必须为 number
  },
build: {
outDir'dist',  // ✅ 类型校验:必须为 string
  },
// plugins: [react()],  // ❌ 若未安装 react 插件,此处会报错
});

不过现在有AI辅助的编辑工具,这些问题就不存在了。

2.2、条件配置:动态返回环境相关配置

通过函数形式接收 { command, mode } 参数,按开发/构建命令或环境模式动态生成配置:

exportdefault defineConfig(({ command, mode }) => {
// command: 'serve'(开发)或 'build'(生产)
// mode: 从 .env 文件读取的模式(如 'development'/'production')

return {
base: mode === 'production' ? '/my-app/' : '/',
server: command === 'serve' ? { 
port3000,
proxy: { '/api': { target'http://dev.api' } }
    } : undefined,
build: {
minify: mode === 'production' ? 'terser' : false,
    }
  };
});

典型场景:开发环境启用代理,生产环境禁用。生产环境开启代码压缩,开发环境关闭以提升构建速度。

2.3、异步配置:支持动态加载外部数据

当配置依赖异步操作(如读取远程配置)时,可导出异步函数:

exportdefault defineConfig(async ({ mode }) => {
const env = await fetchEnvConfig(mode); // 异步获取环境配置
return {
define: {
__APP_VERSION__JSON.stringify(env.VERSION),
    },
server: {
port: env.DEV_PORT,
    }
  };
});

异步逻辑仅在配置解析阶段执行一次,不会影响后续热更新。

1,2、root

root 指项目根目录,可以是一个绝对路径,或者一个相对于该配置文件本身的相对路径,默认值是process.cwd()

它决定了Vite的“工作起点”:Vite会从root指定的目录开始查找:

  • .env环境变量文件
  • tsconfig.json/jsconfig.json类型配置
  • src源码目录
  • public静态资源目录
  • vite.config.ts自身(若配置文件不在根目录需特殊处理)

1、典型使用场景

1.1、标准单项目结构(无需配置)
my-project/
├── .env               # Vite 能自动找到
├── vite.config.ts     # 执行 pnpm dev 的目录
├── src/
└── public/
  • 无需显式设置 root,Vite 默认使用 process.cwd()(即项目根目录)。
  • 验证方法:在 vite.config.ts 中添加 console.log(process.cwd()),输出应为项目根路径。
1.2、Monorepo多包管理(必须配置)
root/
├── packages/
│   ├── app/           # 需要配置为 root 的子项目
│   │   ├── vite.config.ts
│   │   ├── src/
│   │   └── .env       # 子项目自己的环境变量
│   └── shared/        # 其他子包
└── .env               # 根目录的 .env(不应被子项目使用)

正确配置:

// packages/app/vite.config.ts
import { defineConfig, resolve } from'vite';

exportdefault defineConfig({
  root: resolve(__dirname, './'), // 显式指向子项目根目录
});

确保 Vite 仅从当前子项目目录加载配置,避免与根目录或其他子包的 .env 冲突。

1.3、base

base 是 vite.config.ts中控制资源引用路径前缀的核心配置项,仅在生产构建时生效,用于指定应用部署的公共基础路径,若配置错误,会导致CSS/JS/图片等静态资源请求404,其本质是解决“本地开发路径”与“生产部署路径”不一致的问题。

1、基础作用

决定资源URL的前缀:构建时,Vite会为所有静态资源(JS/CSS/图片)的引用路径自动添加base值作为前缀。

示例:

import { defineConfig } from'vite'

exportdefault defineConfig({
    base: '/app/',
    ...
})

则资源路径从/assets/index.js变为/app/assets/index.js,需要注意的是在开发环境会忽略base,仅影响生产环境的构建。

2、通过环境变量动态设置(推荐)

// vite.config.ts
import { defineConfig, loadEnv } from'vite';

exportdefault defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
    base: env.VITE_BASE_URL || '/'// 从 .env 文件读取
  };
});

环境文件示例:

# .env.production
VITE_BASE_URL=/my-app/  # 生产环境部署路径

一次构建,多环境部署:通过 CI/CD 注入不同 VITE_BASE_URL

注意:如果vite.config.ts的process报错,可能你没有安装node相关包,你需要安装npm i --save-dev @types/node 就可以解决。

3、根据命令动态切换

// command: 'serve' | 'build'
exportdefault defineConfig(({ command }) => ({
base: command === 'build' ? '/prod-path/' : '/',
}));

严格区分开发(vite dev)与生产(vite build)路径,但灵活性低于环境变量方案。

1.4、publicDir

publicDir 是 Vite 配置中用于指定纯静态资源目录的选项,其核心用是将目录内的文件原封不动地复制到构建输出目录的根路径下,且不经过任何构建处理(无哈希、无压缩、无模块化)。

开发环境下Vite开发服务器会将publicDir目录下的文件直接映射到根路径(如public/logo.png可通过/logo.png访问)。

生产环境构建时,该目录内容会被原样复制到输出目录(默认dist/)的根路径,不参与任何构建流程(无哈希命名、无压缩、无Tree-shaking)。

src/assets目录下的资源不同,publicDir中的文件不会被Vite处理为模块,因此无法通过import导入,只能通过绝对路径字符串引用。

1、基础配置

默认值:public(项目根目录下的public文件夹)。

自定义路径:

// vite.config.ts
import { defineConfig } from'vite';
import { resolve } from'path';

exportdefault defineConfig({
  publicDir: 'assets'// 自定义目录名(相对项目根目录)
// publicDir: resolve(__dirname, 'my-public'), // 绝对路径
// publicDir: false, // 完全禁用 publicDir 功能
});

若设为 false,则需通过其他方式(如相对路径)引用资源,且无法使用根路径访问。

2、与base配置的协同

若项目部署在非根路径(如 /my-app/),需设置 base: '/my-app/'

引用路径仍需为/logo.png,Vite会自动在构建时根据base调整为/my-app/logo.png

3、错误用法示例

错误引用方式:

<!-- ❌ 错误:使用相对路径(生产环境可能失效) -->
<imgsrc="./logo.png">

<!-- ❌ 错误:尝试通过 import 导入(public 资源非模块) -->
<script>
import logo from'/logo.png'// 构建会报错
</script>

正确引用方式:

<!-- ✅ 正确:绝对路径(开发/生产环境均有效) -->
<imgsrc="/logo.png">

<!-- ✅ 正确:CSS 中直接使用 URL -->
.logo { background-image: url(/logo.png); }

1.5、resolve

resolve 是 Vite 配置中控制模块解析规则的核心选项,其核心作用是定义项目如何查找和解析导入路径,直接影响开发服务器启动速度、HMR效率及代码可维护性。关键价值在于简化模块导入路径、避免重复依赖、统一TypeScript与构建工具的路径映射逻辑。

Vite 8 新增resolve.tsconfigPaths功能,可自动同步tsconfig.json中的paths配置,实现TypeScript与构建工具的路径别名零配置对齐,但需要注意轻微性能损耗(默认关闭)。

1、路径别名:resolve.alias

  • 作用:将简短标识符(如@)映射到实际目录路径,避免冗长的相对路径(如../../../utils)。
  • 配置规范:
    • 必须使用绝对路径:通过path.resolvefileURLPath生成,相对路径会导致解析错误。
    • 优先级最高:若与tsconfig.json中的paths冲突,alias配置会覆盖TypeScript的路径映射。

典型配置:

import { defineConfig, loadEnv } from'vite'
import path from'path';

exportdefault defineConfig({
    resolve: {
        alias: {
'@': path.resolve(__dirname, 'src'),
        }
    }
})

然后你在使用的时候,就可以用简写引入src的内容,比如src目录下有一个utils文件夹:

import { sum } from'@/utils/sum';

console.log(sum(12));

2、扩展名:extensions

  • 作用:在导入语句中省略文件扩展名时,按顺序尝试匹配的扩展名列表。
  • 默认值:['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']
// 自动匹配sum.ts
import { sum } from'@/utils/sum';

console.log(sum(12));

3、dedupe

resolve.dedupe 是 Vite 配置中一个非常关键但容易被忽视的高级选项。

简单来说,它的作用是强制Vite在预构建依赖时,将指定的包机器所有引用都指向项目根目录下的同一个版本,从而避免同一个包被重复打包进最终的代码中。

核心作用:解决“依赖重复”问题

在复杂的现代前端项目中,依赖树往往非常深。同一个包(例如 lodash 或 vue)可能会因为版本范围匹配或依赖层级不同,出现在 node_modules 的不同位置。

如果不使用 dedupe,会发生什么?

  • 你的项目依赖 vue@3.2.0
  • 你引用的某个第三方UI库(比如some-ui-lib)内部依赖vue@3.2.47
  • 包管理器(npm/yarn/pnpm)可能会在some-ui-lib/node_modules下安装一份vue,同时在根目录node_modules/下保留一份vue

后果:

  • 体积膨胀:打包后的文件中包含了两份 Vue 的代码。
  • 运行时错误(最严重):Vue 的全局状态(如 app.config.globalProperties)是绑定在 Vue 构造函数上的。如果页面使用了根目录的 Vue,而组件库使用了它内部嵌套的 Vue,组件库的组件将无法正确注册或响应,导致应用崩溃或行为异常。

resolve.dedupe 就是为了解决这个问题而生的“强制去重”工具。

什么时候必须使用 dedupe?
1. 使用 Monorepo 架构时(最常见场景)

在 Monorepo(如 Turborepo, pnpm workspace)中,你有多个包(packages)

  • packages/app (你的主应用) 依赖 react。
  • packages/ui (你的组件库) 也依赖 react。

虽然现代包管理器(如 pnpm)通常会提升依赖,但在某些情况下,或者当版本不完全匹配时,packages/ui 可能会引用到它自己目录下的 react。

此时,你必须在 app 的 vite.config.ts 中配置:

exportdefault defineConfig({
  resolve: {
// 强制将 'react' 和 'react-dom' 指向根目录的版本
    dedupe: ['react''react-dom'],
  },
});
2. 第三方库打包不规范时

有些第三方库在发布时,没有将某些核心依赖(如 lodashdate-fns)设为 peerDependencies(对等依赖),而是直接打包进了自己的代码,或者在其 package.json 中声明了与你不一致的版本。 如果你的应用和这个库对同一个库的引用指向了不同的物理路径,就会导致逻辑错误。

工作原理

当你配置 resolve.dedupe: ['some-pkg'] 时,Vite 会做以下事情:

  • 1、拦截解析:无论代码中哪里引用了 some-pkg(无论是直接引用,还是深层依赖引用),Vite 都会忽略 node_modules 中的嵌套路径。
  • 2、强制指向:Vite 会强制从项目根目录的 node_modules 中解析 some-pkg。
  • 3、预构建一致性:在 node_modules/.vite 缓存生成阶段,确保 some-pkg 只有一个入口。
配置示例

假设你在开发一个基于 Vue 的大型系统,并且使用了 Monorepo 结构,你可以这样配置:

// vite.config.ts
import { defineConfig } from'vite'
import vue from'@vitejs/plugin-vue'

exportdefault defineConfig({
  plugins: [vue()],
  resolve: {
// 确保整个项目始终使用根目录下的同一个 Vue 版本
    dedupe: ['vue''vue-router''pinia'],
  },
})

4、tsconfigPaths

resolve.tsconfigPaths 是 Vite 8 中引入的一个非常实用的新特性,简单来说,它的作用就是让 Vite 自动读取tsconfig.json里的paths配置,并自动应用为路径别名。

在 Vite 8 之前,如果你想在代码里用 @/components 这种别名,必须分别在 tsconfig.json 和 vite.config.ts 里写两遍配置。现在有了这个功能,你只需要在 TypeScript 里配一次,Vite 就能自动识别,既省事又不会出错。

核心作用

在 Vite 8 之前,如果你想使用路径别名(例如 @/components),你需要做两步工作:

  • 1、配置 tsconfig.json:让 TypeScript 编译器和编辑器(如 VS Code)能识别路径,不报错。
  • 2、配置 vite.config.ts:让 Vite 开发服务器和打包工具能正确解析路径。

resolve.tsconfigPaths: true 的作用就是自动完成第2步,它会读取tsconfig.json中的compilerOptions.paths配置,并将其自动映射为Vite的resolve.alias

详细使用方法

vite.config.ts中开启该选项:

import { defineConfig } from'vite'
import path from'path';

exportdefault defineConfig({
resolve: {
// 开启自动读取tsconfig.json中的paths配置
tsconfigPathstrue,
// 你还可以在这里手动配置额外的别名,手动配置的优先级通常更高
alias: {
'@assets': path.resolve(__dirname, 'src/assets'),
        }
    }
})

配置tsconfig.json,确保你的tsconfig.json中已经配置baseUrlpaths

{
"compilerOptions": {
"target""es2023",
"module""esnext",
"lib": ["ES2023""DOM"],
"types": ["vite/client"],
"skipLibCheck"true,

/* Bundler mode */
"moduleResolution""bundler",
"allowImportingTsExtensions"true,
"verbatimModuleSyntax"true,
"moduleDetection""force",
"noEmit"true,

/* Linting */
"noUnusedLocals"true,
"noUnusedParameters"true,
"erasableSyntaxOnly"true,
"noFallthroughCasesInSwitch"true,
/* 核心配置:用于配置别名 */
"paths": {
"@/*": ["./src/*"// 设置别名
    }
  },
"include": ["src"]
}

配置完成后,你可以直接在代码中使用别名,Vite会自动解析:

import { sum } from'@/utils/sum';
import hero from'@assets/hero.png';

console.log(sum(12), hero);

1.6、clearScreen

clearScreen 是Vite配置中一个控制终端输出行为的选项,默认值为true

作用:决定Vite启动开发服务器或执行构建时,是否自动清除终端屏幕的已有内容。

如果你设置为false,Vite启动后,终端会保留之前的命令输出(如环境变量、错误日子等),新内容追加在下方。

1.7、envDir

envDir 是 Vite 配置中用于指定环境变量文件存放目录的选项。默认情况下,Vite会从项目根目录加载.env系列文件,通过设置envDir,可以将环境变量几种管理到自定义目录(如env/config/env/),使项目结构更清晰,同时避免根目录文件过多导致的混乱。该配置仅影响Vite对环境文件的查找路径,不会改变环境变量的加载规则和优先级。

1、自定义环境文件存储位置

默认行为:Vite会从项目根目录加载.env.env.development等文件。

自定义配置设置envDir: './config/env'后,Vite会从项目根目录/config/env/目录下查找环境文件。

vite.config.ts中设置envDir

import { defineConfig } from'vite';

exportdefault defineConfig({
  envDir: './config/env'// 相对路径(相对于项目根目录)
});

支持相对路径(如 ./env)或绝对路径(如 path.resolve(__dirname, 'env')),必须是字符串,不能是动态计算的路径(Vite 需在解析配置前确定路径)。

目录结构示例
项目根目录/
├── config/
│   └── env/               # envDir 指定的目录
│       ├── .env           # 通用环境变量
│       ├── .env.development
│       ├── .env.production
│       └── .env.test
├── src/
├── vite.config.ts
└── package.json

2、在 vite.config.ts 中读取环境变量

若需在配置中使用环境变量(如动态设置端口),必须手动调用 loadEnv:

import { defineConfig, loadEnv } from'vite';
import path from'node:path';

exportdefault defineConfig(({ mode }) => {
// 重点:loadEnv 的路径必须与 envDir 一致
const env = loadEnv(mode, path.resolve(__dirname, 'config/env'));

return {
envDir'./config/env'// 声明环境文件位置
server: {
portNumber(env.VITE_PORT) || 3000// 从环境变量读取端口
    },
  };
});

loadEnv 的第二个参数需显式指定与 envDir 相同的绝对路径,否则可能因工作目录差异导致读取失败。

1.8、envPrefix

envPrefix 是 Vite配置中控制环境变量保留范围的核心安全机制,默认情况下,Vite 仅将VITE_开头的环境变量通过import.meta.env暴露给客户端代码;通过配置envPrefix,可自定义前缀规则,严格限制敏感信息(如数据库密码、API密钥)意外泄露到浏览器端。这是Vite区别于其他构建工具的关键安全设计。

1、默认安全策略

暴露规则:只有以VITE_开头的环境变量会被注入客户端代码。

示例:

# .env 文件
VITE_API_URL=https://api.example.com  # ✅ 客户端可访问
DB_PASSWORD=secret123 
console.log(import.meta.env.VITE_API_URL); // "https://api.example.com"
console.log(import.meta.env.DB_PASSWORD);  // undefined

暴露的对象里面找不到这个DB_PASSWORD键值,所以为undefined

2、自定义前缀的必要性

  • 当项目须兼容其他框架(如React的REACT_APP_前缀)时,可通过多前缀配置避免重命名成本。
  • 大型项目中不同子模块可能使用不同前缀(如BRAND_A_BRAND_B_),通过envPrefix隔离变量作用域。
  • 强制要求变量以团队约定前缀(如APP_)开头,提升代码可维护性。
基础语法

单前缀配置:

// vite.config.ts
exportdefault defineConfig({
  envPrefix: 'APP_'// 单前缀配置
});

多前缀配置:

exportdefault defineConfig({
  envPrefix: ['VITE_''COMPANY_'], // 数组形式(推荐)
});

注意:只有以envPrefix指定前缀开头的变量会被注入import.meta.env,前缀匹配区分大小写(如envPrefix: 'app_',无法匹配APP_VAR`)。

1.9、开发服务器选项:server

server 是vite.config.ts中控制开发服务器行为的核心配置项,仅在开发环境生效,用于定制本地开啊服务器的端口、代理、跨域等行为。

作用:解决开发阶段的接口跨域问题、局域网访问需求及环境适配,不影响生产构建。

1、核心参数

配置项
类型
默认值
作用说明
host
string/boolean localhost
指定服务器监听地址:
-‘0.0.0.0’:允许局域网访问
-true:自动解析本机IP(等效’0.0.0.0’)
– false:仅限localhost访问
port
number
5173
端口,也可以自定义开发服务器端口,若端口被占用:
– strictPort: true → 强制报错退出 
 – strictPort: false(默认) → 自动尝试5174等后续端口
open
boolean/string
false
启动后自动打开浏览器:
– true: 打开默认地址(如http://localhost:5173
– ‘/about’:当启动服务器自动打开指定子路径
https
boolean/https.ServerOptions
false
启用 HTTPS:
 – true:自动生成临时证书 
 – 对象形式:手动指定证书路径

示例:

import { defineConfig } from'vite'

exportdefault defineConfig({
    server: {
        host: '0.0.0.0'// 允许局域网内其他设备访问
        port: 3000// 自定义端口
        strictPort: true// 如果端口被占用,则报错
        open: true// 启动自动打开
    },
})

2、代理配置

我们在本地开发项目,往往需要配置相关代理来请求后端API,因为如果不配置代理,肯定是会跨域的。

server: {
proxy: {
// 将所有以 /api 开头的请求代理到目标服务器
'/api': {
target'https://backend.example.com',  // 后端接口地址
changeOrigintrue,                   // 修改请求头中的 Origin 为目标地址
wstrue// 启用websocket代理
rewrite(path) => path.replace(/^\/api/''), // 重写路径(去掉 /api 前缀)
securefalse,                        // 忽略 HTTPS 证书验证(若后端用自签名证书)
    }
  }
}
今天的分享就到这里啦,如果觉得有帮助就点个赞 + 关注吧,一起来学习。