首页
/ Vite 2.0 技术解析:框架无关核心、依赖预构建、CSS 一等支持与 SSR 的落地

Vite 2.0 技术解析:框架无关核心、依赖预构建、CSS 一等支持与 SSR 的落地

2026-09-06 14:18:48作者:秋泉律Samson

本文以 Vite 官方 2.0 发布公告为骨架,完整拆解 Vite 2.0 的五大核心能力——框架无关的核心架构、Rollup 兼容的新插件体系、esbuild 驱动的依赖预构建、一等公民级 CSS 支持以及实验性 SSR——并结合当前仓库中的源码实现(依赖预构建调度器SSR 模块加载legacy 插件 等)逐项印证这些设计在代码层面是如何落地的。读完本文,你将理解 Vite 2.0 各特性的设计动机与实现原理,并能对照仓库源码定位其关键实现位置。

背景:Vite 2.0 在 Vite 演进史中的位置

Vite(法语中"快速"的意思,发音 /vit/)是一种面向前端 Web 开发的新型构建工具:可以理解为一个预配置好的开发服务器 + 打包器组合,但更轻、更快。它利用浏览器的原生 ES Modules 支持,以及 esbuild 等编译到原生语言的编译工具,提供流畅的现代开发体验。

公告中特别强调了一点:由于团队在 1.0 发布前就决定彻底重构内部实现,Vite 2.0 实际上是 Vite 的第一个真正稳定的版本(first stable release)。也就是说,1.x 时期的快速迭代都被视为 RC 前的探索,2.0 才是架构定型后的起点。公告同时建议:

当前仓库中的 vite 包 版本已演进到 8.x(描述为 "Native-ESM powered web dev build tool"),底层工具链也已从 esbuild + Rollup 统一迁移到 Rust 编写的 Rolldown。但 2.0 奠定的五大能力——框架无关核心、插件格式、依赖预构建、CSS 支持与 SSR——至今仍是 Vite 的架构支柱,这也是本文以 2.0 公告为主线的价值所在。

框架无关的核心(Framework Agnostic Core)

从 Vue 专用原型到框架无关

公告中回顾了 Vite 的起源:最初它是一个"服务于 Vue 单文件组件(SFC)的原生 ESM hack 原型",Vite 1 则是这个思路的延续,在其上实现了 HMR(热模块替换)。

Vite 2.0 吸取了早期经验,从零重新设计了内部架构,核心原则是:框架无关(framework agnostic),所有框架特定的支持全部委托给插件处理。公告发布时已提供 Vue、React、Preact、Lit Element 的官方模板,Svelte 集成也在社区推进中。

在当前仓库中的印证

这一"框架无关"的设计在仓库目录结构中依然清晰可见:

  • packages/create-vite 中的官方模板覆盖 vanilla、vue、react、preact、lit、qwik、solid、svelte 等全家桶,每个框架各有一对 JS/TS 模板(如 template-reacttemplate-react-ts),模板本身只是静态脚手架,不依赖 Vite 内部的框架逻辑;
  • 框架相关能力全部以插件形式存在:仓库根目录下的 monorepo 包含 plugin-legacy 等官方插件包,而 Vue/React 等框架编译器插件则交由各自的社区插件包(如 @vitejs/plugin-vue 等,不在本仓库内)实现,Vite 内核只负责加载与编排插件。

create-vite 的 package.json 可见,模板包本身只是一个 CLI(create-vite / cva 命令),职责就是复制模板并安装依赖——这正是"核心不感知框架"理念的体现。

新的插件格式与 API

设计:扩展 Rollup 插件接口

公告指出,新的插件系统"受 WMR(Preact 团队)启发,扩展了 Rollup 的插件接口",关键特性包括:

  1. 与大量现有 Rollup 插件开箱兼容——现有生态的 Rollup 插件可以近乎零成本地在 Vite 中工作;
  2. 插件可以使用 Rollup 兼容的钩子,同时拥有 Vite 特有的钩子和属性,用于调整 Vite 独有行为,例如:
    • 区分 dev(开发)与 build(构建)两种场景;
    • 自定义 HMR 处理逻辑;
  3. 改进后的编程 API,方便在 Vite 之上构建更高层的工具和框架。

在当前仓库中的印证

  • 插件体系的核心实现在 packages/vite/src/node/plugin(插件解析与 hook 调用编排),以及内置插件目录 packages/vite/src/node/plugins 下;
  • 该目录下的 css.tsimportAnalysis.tsesbuild.tsworker.tsmanifest.ts 等文件正是公告中"框架无关核心 + 内置能力插件化"思路的具体产物——每个能力都是一个独立插件,而非硬编码进服务器主流程;
  • 插件 API 与编程 API 的完整文档见 插件 APIJavaScript API

值得注意的架构细节:从 plugins 的组织方式可以推断,Vite 内置插件按"功能域"切分(解析、CSS、资源、Worker、Manifest……),框架作者只需在 Rollup 兼容钩子之上实现自己的插件即可接入整条管线,这正是 2.0 公告所承诺的扩展性。

esbuild 驱动的依赖预构建(Dep Pre-Bundling)

这是 2.0 公告中性能提升最显著的一项。

为什么需要预构建

Vite 是基于原生 ESM 的开发服务器,浏览器会直接以 ESM 方式加载模块。这带来两个必须解决的问题(当前仓库的 依赖预构建文档 完整解释了这一点):

  1. CommonJS / UMD 兼容性:开发时所有代码都以原生 ESM 形式提供,因此必须以 ESM 形式先转换 CJS/UMD 依赖,并做"智能 import 分析",使得对动态赋值导出的 CJS 模块(如 React)的命名导入也能正常工作:

    // works as expected
    import React, { useState } from 'react'
    
  2. 性能:一些包把 ESM 构建拆成数百个内部互相引用的文件(文档以 lodash-es 有 600+ 内部模块为例),如果不预构建,浏览器会同时发起数百个 HTTP 请求,造成网络拥塞、页面明显变慢。预构建将其合并为单一模块后,只需 1 个请求。

2.0 的关键变化:esbuild 取代 Rollup

公告明确指出:此前 Vite 1 用 Rollup 做预构建,2.0 改用 esbuild,使依赖预构建速度提升 10-100 倍。公告给出的参考数据:在一个包含 React Material UI 等重依赖的测试应用上冷启动,M1 MacBook Pro 上从 28 秒降到约 1.5 秒

在当前仓库中的印证

预构建的完整实现位于 packages/vite/src/node/optimizer,包含:

  • optimizer.ts:预构建调度器。可以看到 createDepsOptimizer 会为每个开发环境创建独立的优化器实例,通过 100ms 的 debounce 合并"新发现依赖"触发的重复重打包,并在重打包完成后自动触发页面重载(对应 文档 中"服务器运行中发现新依赖会重新 pre-bundle 并刷新页面"的描述);
  • scan.ts:依赖扫描器,即"自动依赖发现"(爬取源码中的 bare import 作为预构建入口)的实现;
  • rolldownDepPlugin.ts:当前版本中预构建打包器相关的插件逻辑。

这里有一个重要的版本事实:当前仓库中的 Vite 已用 Rolldown(Rust 编写)替代 esbuild 作为预构建引擎——依赖预构建文档 明确写道 "The pre-bundling is performed with Rolldown, so it's typically very fast"。也就是说,2.0 时"esbuild 带来 10-100x 提升"的演进方向,后来进一步延续为"全链路 Rust 化",但预构建的整体模型(发现 → 扫描 → 打包 → 缓存 → 服务)保持不变。

配套的定制能力也值得掌握(详见 dep-optimization-options):

  • optimizeDeps.include / exclude:显式包含/排除依赖,典型场景是插件 transform 后才产生的、初次扫描发现不了的 import;
  • 缓存:预构建产物缓存于 node_modules/.vite,是否重跑取决于 lockfile 内容、patches 目录修改时间、vite.config.js 中相关字段、NODE_ENV 等;可用 --force 强制重打包。

一等公民级的 CSS 支持

公告列出了 Vite 2.0 对 CSS 的三项开箱即用能力,这些能力在 内置 CSS 插件 中实现:

  1. 解析器增强(Resolver enhancement):CSS 中的 @importurl() 路径会经过 Vite 的 resolver 增强,使其遵循别名(alias)配置并能解析 npm 依赖中的资源;
  2. URL rebasingurl() 中的路径会自动基于"资源最终服务位置"而非"导入方文件位置"重新计算,因此无论从哪个目录导入同一个 CSS,其相对资源引用都指向正确;
  3. CSS 代码分割(CSS code splitting):一个代码分割的 JS chunk 会同时产出对应的 CSS 文件,请求该 JS chunk 时对应的 CSS 会被自动并行加载。

公告的原始表述(完整继承):

Vite treats CSS as a first-class citizen of the module graph and supports the following out of the box:

  • Resolver enhancement: @import and url() paths in CSS are enhanced with Vite's resolver to respect aliases and npm dependencies.
  • URL rebasing: url() paths are automatically rebased regardless of where the file is imported from.
  • CSS code splitting: a code-split JS chunk also emits a corresponding CSS file, which is automatically loaded in parallel with the JS chunk when requested.

这三项能力在仓库中均有对应的测试用例可验证,例如:

服务端渲染(SSR)支持

公告中的设计要点

Vite 2.0 随附实验性 SSR 支持(完整文档见 SSR 指南),核心设计有三点:

  1. 高效的 ESM 源码加载与更新:Vite 提供一组 API,让 Node.js 端在开发期间能高效加载并更新基于 ESM 的源码——"几乎像服务端 HMR":编辑文件后 SSR 输出立即更新,无需重启;
  2. 依赖外部化(externalization):自动将 CommonJS 兼容的依赖 externalize 掉(即用 Node 原生的 require 加载),从而提升开发服务器响应速度和 SSR 构建速度;
  3. 生产服务器与 Vite 解耦:生产环境的服务端可以完全不依赖 Vite 运行,同一套配置也容易被改造为预渲染 / SSG。

公告还强调:Vite SSR 是作为**低层能力(low-level feature)**提供的,预期是更高层的框架在其之上构建(例如当时的 Nuxt 3、SvelteKit 等)。

在当前仓库中的印证

SSR 相关源码集中在 packages/vite/src/node/ssr

  • ssrModuleLoader.ts:SSR 模块加载器,对应"Node.js 端加载与更新 ESM 源码"的能力;
  • ssrTransform.ts:SSR 场景下的模块转换(将浏览器端的 import/导出语义转译为 Node 可执行的 ESM);
  • ssrManifestPlugin.ts:SSR manifest 生成,支撑"生产服务器与 Vite 解耦"——构建时生成资源清单,生产端按清单按需加载 CSS/资源即可,无需 Vite 本身;
  • fetchModule.ts 等文件则服务于"按需拉取模块"的场景。

依赖外部化的策略由 external.tsnodeResolve.ts 共同决定;行为可用 playground/ssrplayground/ssr-deps 中的测试用例交叉验证(后者专门覆盖 SSR 场景下依赖 externalize 的各种组合)。

可选的 Legacy 浏览器支持(@vitejs/plugin-legacy)

Vite 默认目标是支持原生 ESM 的现代浏览器,但通过官方插件 @vitejs/plugin-legacy 可以**选择性地(opt-in)**为旧浏览器提供兼容。公告中的原始承诺:

The plugin automatically generates dual modern/legacy bundles, and delivers the right bundle based on browser feature detection, ensuring more efficient code in modern browsers that support them.

即:自动生成 modern/legacy 双份构建产物,基于浏览器特性检测分发正确的产物,确保支持 ESM 的现代浏览器继续拿到高效的原生 ESM 代码。

当前仓库中该插件位于 packages/plugin-legacy,README 完整说明了其默认行为与全部选项:

默认行为(引用自 plugin-legacy README):

  • 为最终 bundle 中的每个 chunk 生成对应的 legacy chunk,经 @babel/preset-env 转换并以 SystemJS 模块形式产出(仍支持代码分割);
  • 生成 polyfill chunk,包含 SystemJS 运行时以及根据浏览器目标与实际使用useBuiltIns: 'usage' 检测)决定的必要 polyfill;
  • 向生成的 HTML 注入 <script nomodule> 标签,仅在特性缺失的浏览器中加载 polyfills 与 legacy bundle;
  • 注入 import.meta.env.LEGACY 环境变量(仅 legacy 生产构建中为 true)。

典型用法

// vite.config.js
import legacy from '@vitejs/plugin-legacy'

export default {
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11'],
    }),
  ],
}

核心选项速览(完整说明见 plugin-legacy README):

选项 类型 默认值 说明
targets string | string[] | { [key: string]: string } 'last 2 versions and not dead, > 0.3%, Firefox ESR' 传给 @babel/preset-env,决定 legacy chunk 的语法降级目标,兼容 Browserslist 语法,会自动读取项目的 browserslist 配置
modernTargets string | string[] 'edge>=105, firefox>=106, chrome>=105, safari>=16.4, chromeAndroid>=105, iOS>=16.4' 收集 modern chunk polyfill 时的目标;仅在 renderLegacyChunksfalse 时使用
polyfills boolean | string[] true 是否/如何生成 legacy polyfill chunk;传字符串数组可显式指定 core-js 模块
additionalLegacyPolyfills / additionalModernPolyfills string[] 手动追加 DOM API polyfill(usage 检测只覆盖 ES 语法特性)
modernPolyfills boolean | string[] false 为 modern 构建单独生成 polyfill chunk;README 明确提示 core-js@3 的自动检测非常激进,不建议无脑开 true
renderLegacyChunks boolean true 设为 false 可只输出 modern 构建,把插件降级为"仅向 modern 构建注入 polyfill"
externalSystemJS boolean false 设为 true 时不内联 systemjs/dist/s.min.js,改为外部引用
renderModernChunks boolean true 设为 false 时只输出 legacy bundle(也适用于 file: 协议下本地打开的场景)

另外两点实操细节:使用 terser 压缩时需安装 terser;若项目有严格 CSP,需引入导出的 cspHashes 将内联脚本哈希加入 script-src 白名单(值会随次版本变化,建议动态生成而非硬编码)。

测试验证可参考 playground/legacy(含 __tests__ 下的 10 个测试文件,覆盖多输出、自定义文件名、modern target 等场景)。

快速上手:一分钟启动 Vite 应用

公告给出的入门方式(发布时要求 Node.js >=12):

npm init @vitejs/app

需要说明的适用前提npm init @vitejs/app 是 Vite 2.0 时代的命令;在当前仓库对应的版本中,官方脚手架已演进为 create-vite,命令为:

npm create vite@latest my-app

当前 create-vite 包bin 字段暴露了 create-vitecva 两个命令,且 engines 要求 node ^20.19.0 || >=22.12.0。模板目录(template-vanillatemplate-vuetemplate-reacttemplate-svelte 等)均可在 packages/create-vite 下直接查看,每个模板都附带最小可运行的 index.htmlpackage.json 与框架入口文件,是理解"框架无关核心 + 模板即框架适配层"最直接的教材。

小结:2.0 五大能力在今天的映射

2.0 能力 2.0 时的形态 当前仓库中的位置
框架无关核心 内核去 Vue 化,框架能力插件化 packages/create-vite 模板族 + 外部框架插件
新插件格式 扩展 Rollup 插件接口,兼容现有 Rollup 插件 packages/vite/src/node/plugin.ts内置插件
依赖预构建 esbuild 引擎,10-100x 提速 packages/vite/src/node/optimizer(引擎现为 Rolldown)
一等 CSS 支持 解析增强 / URL rebasing / CSS 代码分割 css.tsplayground/css-codesplit
SSR 支持 实验性,低层 API packages/vite/src/node/ssrplayground/ssr
Legacy 浏览器 @vitejs/plugin-legacy 双产物 packages/plugin-legacy

Vite 2.0 公告的价值在于它定义了 Vite 从"快而巧"走向"稳而全"的架构基线:框架无关让它可以成为任何框架的底座,Rollup 兼容的插件格式让生态平滑迁移,预构建让原生 ESM 开发体验可用,CSS 与 SSR 补齐了生产级需求。对照当前仓库源码可以确认:这些能力不仅被保留,而且被持续加深——预构建从 esbuild 演进到 Rolldown,SSR 从实验性能力成长为支撑整个生态框架的核心运行时,这正是阅读 2.0 公告与源码对照时最值得体会的一点。

登录后查看全文
热门项目推荐
相关项目推荐