写过八年 Vite 配置,今天才发现「环境」这件事根本就没被认真管过——Vite 7 把这件事彻底变了
写过八年 Vite 配置,今天才发现「环境」这件事根本就没被认真管过——Vite 7 把这件事彻底变了
配 Vite 配了八年,你大概经历过这几个阶段:第一个阶段是 SPA 阶段,所有东西都往 client 环境塞,没感觉有什么不对;第二个阶段是接 SSR,发现 server.ssrLoadModule 和普通构建是两套逻辑,但勉强能跑;第三个阶段是接边缘计算(Cloudflare Workers、Vercel Edge),发现浏览器跑一套、Node 跑一套、Edge 又是一套,三套构建、三套配置、三个坑。
这个阶段一过,你就开始在 vite.config.ts 里堆 if/else,在插件里打环境判断补丁,整个人变成了 Vite 配置工程师。
2026 年,Vite 7 正式把这件事收口了——Environment API 从实验阶段毕业,成为一等公民。
以前的问题在哪
Vite 5 及之前,构建目标只有一个隐式的「客户端环境」,SSR 靠顶层的 ssr 配置项打补丁。看起来能用,但问题藏在细节里:
模块图是共享的。 server.moduleGraph 同时服务 client 和 ssr,但两个环境的依赖解析逻辑完全不同——client 用浏览器条件(browser、development),SSR 用 Node 条件(node、require)。混在一起,模块解析结果就会出错。
插件不知道自己在哪个环境跑。 同一个插件的 transform 钩子,对 client 代码和 SSR 代码可能需要不同的处理逻辑,但插件拿不到这个上下文,只能靠读文件路径猜,或者靠 import.meta.env.SSR 这个 hack 判断。
每个新运行时都要新插件。 接 Cloudflare Workers 要装 @cloudflare/vite-plugin,接 Vitest 要装对应插件,它们各自造了一套环境抽象,互不兼容。
Vite 7 Environment API 是什么
一句话:每个运行时环境有自己独立的模块图、插件管道和构建配置,配置层统一表达,运行时按需实例化。
// vite.config.ts — Vite 7 标准写法
import { defineConfig } from vite
import react from @vitejs/plugin-react
export default defineConfig({
plugins: [react()],
environments: {
// 浏览器端
client: {
build: { outDir: dist/client }
},
// Node.js SSR
ssr: {
dev: { optimizeDeps: { include: [react-dom/server] } },
build: { outDir: dist/server, target: node20 }
},
// RSC / 服务端组件
rsc: {
build: { outDir: dist/rsc, noExternal: true }
},
// 边缘运行时
edge: {
resolve: { noExternal: true, conditions: [edge, worker] },
build: { outDir: dist/edge }
}
}
})
这四个环境,在同一个 Vite dev server 里同时启动,client 环境用浏览器通过 HTTP 请求模块,ssr 环境用 Node.js Module Runner 直接调用模块,edge 环境连接到 workerd 运行时。每个环境有自己独立的 EnvironmentModuleGraph,解析条件完全隔离。
插件终于知道自己在哪个环境了
这是最被低估的变化。以前写插件,判断运行环境只能靠:
// 以前的土办法
if (id.includes(node_modules) || import.meta.env.SSR) { ... }
现在,插件钩子里直接拿到 this.environment:
const myPlugin = (): Plugin => ({
name: my-plugin,
transform(code, id) {
// Vite 7 直接告诉你当前在哪个环境
if (this.environment.name === client) {
// 处理浏览器端代码
} else if (this.environment.name === ssr) {
// 处理 SSR 代码
} else if (this.environment.name === edge) {
// 处理 Edge 代码
}
}
})
插件作者不再需要为每个运行时写独立插件,一个插件可以按环境分支处理。@cloudflare/vite-plugin 就是基于这个 API 重构的——同一个插件,同时负责本地 workerd 开发环境和线上 Edge 构建。
buildApp 钩子:多环境构建编排
生产构建也有变化。以前 SSR 构建靠 vite build --ssr,和 client 构建完全独立,没有协调机制。
Vite 7 引入了 builder.buildApp 钩子,框架可以精细控制构建顺序和并行策略:
export default defineConfig({
builder: {
async buildApp(builder) {
const environments = Object.values(builder.environments)
await Promise.all(
environments.map(env => builder.build(env))
)
}
}
})
这让框架作者可以针对不同场景优化:SSR + RSC 场景可以并行构建两个服务端环境,减少总构建时间。
向下兼容,老配置还能用
这是关键设计原则:Vite 7 没有破坏任何现有配置。
如果你写的是纯 SPA/MPA,vite.config.ts 看起来和 Vite 5 一模一样,Vite 内部自动把所有顶级配置映射到 environments.client,不需要改任何代码:
// Vite 5 项目直接升级 Vite 7,什么都不用改
export default defineConfig({
build: { outDir: dist },
optimizeDeps: { include: [react] }
})
老配置自动获得 Vite 7 的构建性能提升(Rolldown 替代 esbuild+Rollup),但环境模型不变。
为什么这对中国前端团队特别重要
国内团队有两个典型场景特别受益:
第一,多部署目标项目。 现在很多项目同时要跑在:浏览器端(Vite 默认)、Node 服务端(Express/Fastify)、阿里云 FC 或腾讯云 SCF 边缘函数。以前每套目标都单独配 Vite 实例,或者靠 define 全局变量 hack 解决。Environment API 让这些在同一个配置文件里表达,运维文档和维护成本都降下来。
第二,微前端 + 模块联邦。 Module Federation 2.0 依赖宿主应用和远程应用各自的构建配置一致。Environment API 让每个环境的构建参数显式表达,不同团队负责不同环境时,配置接口清晰,减少对接摩擦。
下一步怎么落地
如果你现在想迁移,分两步走:
第一步:确认当前 Vite 版本。 Node.js 20.19+ 或 22.12+ 是 Vite 7 的硬要求,老版本先升 Node。
第二步:把 SSR 配置迁移到 environments。 找到现有的 ssr: { ... } 配置,翻译成 environments.ssr: { ... }。Vite 7 会给一个废弃警告,告诉你把 ssr 迁移到 environments。这个翻译是 1:1 的,不涉及业务逻辑变化。
// 旧写法(Vite 5)
export default defineConfig({
ssr: {
target: node,
noExternal: [react-dom]
}
})
// 新写法(Vite 7)
export default defineConfig({
environments: {
ssr: {
build: { target: node20 },
resolve: { noExternal: [react-dom] }
}
}
})
第三步:如果接了 Cloudflare Workers 或 TanStack Start,不需要额外配置。 这些插件内部已经基于 Environment API 写了环境工厂函数,升级 Vite 7 之后插件自动获得完整类型提示和 IDE 支持。
Vite 7 的 Environment API 不是一个花哨的新功能,它是 Vite 历史上第一次把「运行时多样性」这件事在构建层正式承认。以前我们用 Vite 假装只有一个世界,然后靠补丁假装另一个世界也存在。2026 年,不需要假装了——你需要几个环境,Vite 7 就给你几个,每个都是一等公民。
评论区
登录后可评论。