首页
/ Vite 5.1 发布解析:Runtime API、性能飞跃与 API 收敛

Vite 5.1 发布解析:Runtime API、性能飞跃与 API 收敛

2026-09-05 18:30:47作者:咎岭娴Homer

Vite 5.1 发布于 2024 年 2 月 8 日,是 Vite 5 系列中最具技术分量的一个版本:它引入了后来演化为 Module Runner / Environment API 的实验性 Runtime API、让 build.assetsInlineLimit 支持回调函数、将 CSS 预处理器搬进线程池,并在一万模块的冷加载基准上再创纪录。本文基于 Vite 官方发布公告(docs/blog/announcing-vite5-1.md)逐条解析这些特性,并结合当前仓库中的源码实现,说明每一项功能在代码层面是如何落地、以及它如何影响你在 monorepo、SSR 和大型项目中的实际开发体验。

Vite 5.1 一万模块加载时间演进图

版本背景与快速入口

Vite 5 发布于同年 11 月,是 Vite 生态的一次重大跃升。发布 5.1 时,Vite 已达成每周 1000 万 npm 下载量与 900 名贡献者的里程碑。对首次接触 Vite 的读者,官方建议先阅读 Getting Started 指南Features 指南;完整的变更记录见 packages/vite/CHANGELOG.md

Vite Runtime API:与 Server 解耦的模块执行层

5.1 最值得关注的新特性是实验性的 Vite Runtime API:它允许任意代码先经过 Vite 插件管线处理后再执行。它区别于 server.ssrLoadModule 的关键在于运行时实现与 dev server 完全解耦——库作者和框架作者可以在 server 与 runtime 之间自建通信层(message channel、fetch、直接函数调用或 websocket 均可),并且:

  • 支持 SSR 场景下的 HMR;
  • 单个 server 不再受客户端数量限制,每个 client 拥有独立的模块缓存;
  • 不依赖 Node/Bun/Deno 的任何内建 API,可在任意环境运行;
  • 易于集成到自带代码执行机制的工具中(例如可以传入一个用 eval 代替 new AsyncFunction 的 runner)。

这条路线并非凭空而来:最初的设想由 Pooya Parsa 提出,由 Anthony Fu 以 vite-node 包实现,用于支撑 Nuxt 3 的 Dev SSR,随后成为 Vitest 的底座——即“vite-node”这一思想已经被长时间的生产环境验证过。5.1 中的 API 是 Vladimir Sheremet 基于 Vitest 中重新实现 vite-node 的经验所做的新一代迭代,相关设计讨论历时约一年。

重要演进:正如公告中的说明,Vite Runtime API 最终演化为 Module Runner API,作为 Environment API 的一部分在 Vite 6 中正式发布。当前仓库中已经可以完整看到这条演进链:docs/guide/api-environment.md 描述 Environment API,docs/changes/ssr-using-modulerunner.md 记录了 server.ssrLoadModulemoduleRunner.import(url) 取代的迁移说明,核心实现位于 packages/vite/src/module-runner/index.tspackages/vite/src/node/ssr/runtime/serverModuleRunner.ts。如果你今天基于 Vite 5.1 使用该实验 API,迁移路径就是指向这套 Environment/Module Runner 体系。

新特性

.css?url 支持改进

将 CSS 文件以 URL 形式导入(import url from './style.css?url')现在可以可靠且正确地工作。公告特别指出,这是 Remix 迁移到 Vite 所遇到的最后一道障碍(对应 issue #15259)。对于依赖“引用 CSS 资源 URL 而非注入样式”的框架(尤其是服务端渲染框架中按 URL 下发样式的模式),这一修复消除了此前的不确定性。

build.assetsInlineLimit 支持回调

此前 build.assetsInlineLimit 只能是一个字节数阈值;5.1 起可以传入一个返回布尔值的回调,针对特定资源选择内联或走文件,返回 undefined 时则回落到默认逻辑。当前仓库中该逻辑的实现位于 packages/vite/src/node/plugins/asset.ts

const { assetsInlineLimit } = environment.config.build
if (typeof assetsInlineLimit === 'function') {
  const userShouldInline = assetsInlineLimit(file, content)
  if (userShouldInline !== undefined) {
    return userShouldInline
  }
} else {
  limit = Number(assetsInlineLimit)
}

也就是说回调签名为 (file: string, content: string) => boolean | undefined:回调拿到文件路径与二进制内容,决定“是否内联”;不表态(返回 undefined)就把决定权交还给大小阈值。典型用法例如“logo 类 svg 一律走文件、小图标一律内联”。参数完整说明见 docs/config/build-options.md

循环导入下的 HMR 改进

Vite 5.0 中,位于循环导入链内、即使声明了 accept 的模块一旦发生变更也会触发整页刷新。5.1 放宽了这一限制:允许 HMR 在客户端正常应用而无需整页刷新;但若 HMR 应用过程中出现任何错误,仍会回退到整页刷新作为兜底(对应 #15118)。该行为对应的测试场景可见 playground/hmr/accept-exports/ 下的多个用例,其中覆盖了 self-accept、accept 导出等循环依赖组合。

ssr.external: true 强制外部化所有 SSR 包

Vite 历史行为是“外部化所有包、但保留 linked packages(workspace 内软链依赖)”。新选项 ssr.external: true 可以强制把所有包(含 linked packages)都外部化。公告给出两个典型场景:

  • monorepo 测试中,模拟“所有包均被外部化”的常规生产行为;
  • 通过 ssrLoadModule 加载任意文件时,若不在意 HMR,希望包始终按 Node 原生方式 require/import。

当前仓库中该判断入口位于 packages/vite/src/node/external.ts 与 SSR 环境配置解析路径中:ssr.external 被设置为 true 时直接短路“按包名外部化”的默认逻辑。参数说明见 docs/config/ssr-options.md

Preview Server 暴露 close 方法

预览服务器现在暴露 close() 方法,能够正确地拆除整个服务器(包括所有已打开的 socket 连接)。这在 CI 中以编程方式启动 vite preview 并需要干净释放端口的场景中非常关键。实现位于 packages/vite/src/node/preview.tscloseServer 先摘除 SIGTERM 监听器,再关闭 HTTP server,最后才按序执行 closePreviewServer 插件钩子——即插件可以在服务器完全拆除后才做自己的清理。

性能改进

Vite 官方使用 vite-dev-server-perf 对 Vite 4.0 以来所有小版本测量了“加载 1 万个模块(25 层深树)”的耗时。每个模块是一个带计数器的小 TypeScript 文件并互相导入,因此该基准主要度量 bundle-less 架构下以独立模块逐个请求的开销。演进结果:Vite 4.0 在 M1 MAX 上耗时 8 秒;Vite 4.3 聚焦性能后降到 6.35 秒;Vite 5.1 进一步降到 5.35 秒(Headless Puppeteer 测量,用于版本间对比,不代表用户真实体感)。在 Chrome 无痕窗口中的实测对比:

1 万模块指标 Vite 5.0 Vite 5.1
加载时间 2892ms 2765ms
加载时间(缓存) 2778ms 2477ms
整页刷新 2003ms 1878ms
整页刷新(缓存) 1682ms 1604ms

线程化运行 CSS 预处理器

Vite 5.1 加入按需启用的 CSS 预处理器线程池,通过 css.preprocessorMaxWorkers: true 开启(对应 #13584)。官方报告:对 Vuetify 2 项目,开启后 dev 启动时间减少约 40%。当前仓库中该配置的定义与默认值在 packages/vite/src/node/plugins/css.ts

// CssPostPluginConfig
preprocessorMaxWorkers?: number | true
// 默认配置
preprocessorMaxWorkers: true,

从这份源码结构看,该选项的语义是“最大 worker 数,true 表示取默认上限”,且当前版本已将其默认置为 true——即 5.1 引入时“opt-in”的特性在后续版本已成为默认行为。开启方式与参数说明见 docs/config/shared-options.md

optimizeDeps.holdUntilCrawlEnd: false:新依赖发现策略

5.1 提供了 optimizeDeps.holdUntilCrawlEnd 选项。默认策略下,dev server 会等 HTML 爬取(crawl)结束后才开始启动依赖预构建;将其设为 false 则切换到“边爬取边发现依赖”的新策略,官方表示这在大型项目中可能改善冷启动,并正在考虑未来将其设为默认。当前仓库中该逻辑位于 packages/vite/src/node/optimizer/optimizer.tspackages/vite/src/node/optimizer/index.ts 的发现(discovery)流程中:开启新策略后,预构建不再以“爬取完成”为前置门控,从而让首请求更早命中已扫描到的依赖。

解析路径缓存(fs.cachedChecks)默认启用

针对模块 ID 解析的文件系统探测,5.1 将 fs.cachedChecks 优化默认打开(#15704)。官方数据:Windows 上 tryFsResolve 提速约 14 倍,三角(triangle)基准下的整体 ID 解析提速约 5 倍。这与 5.1 整体解析提速的方向一致——减少重复的文件系统 exists/stat 调用。

其他内部性能改进

公告列举了 dev server 的若干增量优化,均可在当前仓库的 server 源码脉络中对应到:

  • 新增短路 304 响应的中间件(#15586),避免重复读取磁盘内容;
  • 在热路径上避免调用 parseRequest(#15617),减少每次请求的 URL 重复解析开销;
  • Rollup 改为正确的懒加载(#15621),dev server 启动不再为 build 依赖付出加载成本。

弃用与移除

Vite 团队持续在收敛 API 表面以保证长期可维护性,5.1 有两项重要决定:

import.meta.globas 选项弃用

随着浏览器标准转向 Import Attributes,import.meta.glob 中的 as 选项被弃用。官方不会引入替代选项,而是建议改用 query(例如 import.meta.glob('./dir/*.css', { query: '?raw' }))。从源码结构看,该弃用由 packages/vite/src/node/deprecations.ts 统一输出警告,glob 参数解析在 importGlobPlugin 中完成。参数语义详见 docs/guide/features.md

移除实验性构建期预构建(build-time pre-bundling)

Vite 3 引入的实验性“构建时依赖预构建”在 5.1 被移除。官方的理由很清晰:Rollup 4 已切换到原生解析器,Rolldown 又在同步推进,该特性赖以成立的“构建性能”与“dev/build 不一致”两个前提都不再成立。团队结论是:用 Rolldown 同时承担“dev 依赖预构建”与“生产构建”是更好的路径,且 Rolldown 有可能实现比依赖预构建高效得多的构建期缓存(#15184)。dev 与 build 依赖预构建机制的现行说明见 docs/guide/dep-pre-bundling.md

参与与致谢

Vite 团队感谢 900 名核心贡献者以及插件、集成、工具与翻译的维护者;贡献入口见 CONTRIBUTING.md。Vite 5.1 的完成同样离不开 StackBlitz、Nuxt Labs 与 Astro 对团队成员的雇佣支持,以及 GitHub Sponsors 与 Open Collective 上的赞助者。

小结

  • Runtime API 是 5.1 的战略性特性:它把“经 Vite 管线处理代码”从 dev server 中剥离出来,直接铺垫了后来的 Module Runner 与 Environment API(Vite 6+),是当前仓库中 packages/vite/src/module-runner/ 一脉的直接前身;
  • 回调式 assetsInlineLimitssr.external: true、preview close() 是三个面向工程化的实用 API,分别解决资源内联策略、SSR 外部化语义与程序化生命周期管理;
  • 性能层面,5.1 通过 CSS 预处理线程化(现已默认开启)、optimizeDeps.holdUntilCrawlEnd 新策略、解析路径缓存与 304 短路等组合拳,把 1 万模块加载时间压到 5.35 秒;
  • 收敛层面,弃用 globas、移除构建期预构建,表明 Vite 正朝着“dev/build 统一由 Rolldown 驱动”的方向做减法。
登录后查看全文
热门项目推荐
相关项目推荐