前端知库

Vite Environment API 解决了什么问题?

Vite 5 之前开发服务器里只有 client 和可选的 ssr 两个隐式环境,SSR 代码都在 Vite 自己的 Node 进程里执行,和生产上的 workerd 这类运行时对不上。Vite 6 引入 Environment API,可以按生产架构声明任意多个环境,每个环境有自己的配置、模块图和运行时,由 ModuleRunner 去取转换后的代码执行。普通 SPA 不用改配置,它主要给框架、插件和运行时提供方用,目前整体处于候选发布阶段。

参考答案

简答

比如一个应用部署在 Cloudflare Workers 上,服务端代码在生产环境跑在 workerd 里。可是开发时,Vite 的 SSR 代码是在开发服务器自己的 Node 进程里执行的,Node 有的 API workerd 不一定有,本地正常、上线报错的问题就会出现。

原因是 Vite 5 之前只有两个隐式的环境:client,以及可选的 ssr。一个应用如果同时有浏览器、Node 服务器和边缘服务器三种运行环境,Vite 5 没办法把它们分别表示出来。

Vite 6 引入的 Environment API 让开发服务器能同时管理多个环境。在配置的 environments 里按生产架构声明,比如 server 和 edge,每个环境可以有自己的解析规则和构建选项,连接自己的运行时;浏览器以外的环境由 ModuleRunner 去取转换后的代码来执行,workerd 环境本地可以接 miniflare。

要注意,普通 SPA 不需要改任何配置,顶层选项默认就作用在 client 环境上。这套 API 主要给框架作者、插件作者和运行时提供方用。官方文档写明它整体处于候选发布阶段,部分 API 仍是实验性的,稳定前可能有破坏性变更。

以前的 SSR 为什么和生产对不上

Vite 开发 SSR 应用时,浏览器那边直接用原生 ES 模块加载转换后的代码,服务端那边则在 Vite 开发服务器所在的 Node 进程里执行。如果生产服务器也是 Node,这没什么问题。

可是现在很多服务端代码跑在别的运行时里,官方文档举的例子是 Cloudflare 的 workerd,它的限制和 Node 不一样。还有的应用前端在浏览器、一部分接口在 Node、一部分在边缘节点。Vite 5 只能区分“客户端”和“SSR”两类,开发环境模拟不出这种结构。

Vite 自己为什么开发时快、生产时还要打包,见《Vite 开发模式为什么启动快,生产环境为什么仍需要构建?》。

怎样声明多个环境

普通 SPA 或多页应用不用动配置,顶层的 build、resolve 等选项会自动应用到 client 环境。需要多个运行环境时,在 environments 里声明:

JavaScript
export default {
  build: {
    sourcemap: false,
  },
  optimizeDeps: {
    include: ['lib'],
  },
  environments: {
    server: {},
    edge: {
      resolve: {
        noExternal: true,
      },
    },
  },
};

这里有一个读者会疑惑的地方:顶层的 build.sourcemap 会不会影响 server 和 edge?会。声明的环境默认继承顶层选项,所以 server 和 edge 都不生成 sourcemap,edge 另外把所有依赖打进包里。但有少数选项不继承,比如 optimizeDeps 只作用于客户端,因为依赖预构建是给浏览器用的。官方建议客户端的配置仍然写在顶层,不要写进 environments.client。

开发时 client 和名为 ssr 的服务端环境总是存在;构建时,只有配置了 environments.ssr 或 build.ssr 才会有 ssr 环境。顶层的 ssr 选项计划在 API 稳定后废弃。

多个环境在开发服务器里怎样运行

所有环境共用同一个 HTTP 服务器、中间件、解析后的配置和插件流水线,但每个环境连接自己的运行时。浏览器直接导入客户端代码;其他环境用 ModuleRunner,由它向开发服务器取回转换后的模块再执行。

这样,Cloudflare 的 Vite 插件这类运行时提供方就可以提供一个自定义环境,在本地用 miniflare 跑 workerd,开发时就能拿到和生产一致的 API 限制。插件在处理模块时,也能知道当前在为哪个环境转换,按环境输出不同的代码。Vite 插件钩子本身的作用见《Vite 插件的 resolveId、load 和 transform 分别做什么?》。

常见追问

写业务应用需要马上迁移到 Environment API 吗?

不需要。官方文档明确说暂不推荐切换,原有的服务端 API 和 Vite 5 保持向后兼容,简单应用也不需要新配置。通常是框架或部署平台的插件在背后用它,比如 React Router v8 的 framework mode 就默认启用了 Vite Environment API。

ModuleRunner 和以前的 ssrLoadModule 是什么关系?

都是在服务端环境里加载转换后的模块。官方文档提到,SSR 计划改为基于 ModuleRunner API,ModuleRunner 可以在不同的运行时里执行,而不只是 Vite 自己的 Node 进程。

client 环境的配置为什么建议写在顶层?

因为顶层选项本来就作用于 client,而且会被其他环境继承。写在顶层,SPA 的配置方式和以前一样,多环境时也只需要在 environments 里写各环境的差异。