前一篇说 Vite 是开发服务器、构建管线与插件平台,而不是完整的 React 框架。那么文件系统路由、Server Components、Server Actions、SSR、ISR 这些框架能力从哪里来?Vinext 给出了一种答案:在 Vite 之上重新实现 Next.js 的公开 API,让 Next.js 风格的应用可以由 Vite 的工具链运行。

这句话里最重要的是「重新实现」。Vinext 不会调用 next build 再把 Next.js 产物搬到别处;它实现了路由、next/* 常用模块、渲染入口和 CLI 所需的兼容层,然后把 RSC、SSR 与客户端构建交给 Vite。因此,不能简单把它说成「Next.js 换了个 bundler」;它是一个以兼容 Next.js 开发模型为目标的 Vite 框架运行时。

Vinext 想解决什么问题

现代 React 项目常常同时希望拥有两类能力:

  • Vite 的原生 ESM 开发、模块级 HMR 和插件生态;
  • Next.js App Router 的目录路由、RSC、Server Actions、Route Handlers、SSR / ISR 等 Web 框架约定。

Vinext 尝试把它们放到同一套工具链里。它能够识别 app/ 或 pages/ 目录,读取 next.config.*,并让许多熟悉的约定继续成立:

TEXT
app/
  layout.tsx
  page.tsx
  posts/
    [slug]/
      page.tsx
  api/
    revalidate/
      route.ts

常见脚本也保持接近 Next.js:

JSON
{
  "scripts": {
    "dev": "vinext dev",
    "build": "vinext build",
    "start": "vinext start"
  }
}

这降低的是工具链和路由层的迁移成本,不是把所有 Next.js 行为自动变得百分之百相同。官方 README 明确标注 Vinext 仍在积极开发中,尤其是较新的 App Router 特性、缓存与平台相关行为,必须按自己的应用逐项验证。

RSC 为什么至少需要三套构建环境

普通 SPA 常常只需一个浏览器 bundle;RSC 应用则至少同时面对三种代码去向:

环境 执行什么 不能做什么
RSC 环境 运行 Server Components,读取数据并生成 RSC Payload 不能把组件交互代码作为服务端组件执行
SSR 环境 根据 RSC 结果生成首次访问的 HTML 不能把它误认为浏览器 hydration 代码
客户端环境 加载 Client Components 并 hydration,响应事件 不能读取服务端密钥或直接访问数据库

当请求到达 App Router 页面时,可以把流程概念化为:

TEXT
请求
  -> RSC 入口:匹配路由,执行 layout/page,生成 RSC Payload
  -> SSR 入口:把 Payload 渲染为初始 HTML
  -> 浏览器:下载 Client Component 代码并 hydration

Vinext 的 vinext build 会为 App Router 做 RSC、SSR 与客户端的多环境构建;@vitejs/plugin-rsc 处理如 "use client"、"use server" 这样的模块边界和 RSC 流。现在再回看 Vite 的「模块图」概念,就不难理解为什么一个文件的导入方向很重要:客户端组件直接导入的模块会进入浏览器图,服务端专属代码则必须停留在服务器图里。

"use client" 仍然不是「只在客户端运行一次」的开关,而是模块图的边界。把它写在页面顶层,会把整个下游依赖链推向客户端;把交互收缩到叶子组件,RSC 才能减少发给浏览器的代码。这条规则与 Next.js App Router 相同。

它与 Next.js、OpenNext 的关系

三个名字容易混在一起,但路线不同:

方案 构建与运行路线 更适合什么情况
Next.js 使用官方框架与官方编译/运行时路径 需要最广 API 兼容、最新官方能力与成熟支持
Vinext 在 Vite 上重新实现 Next.js 的公开 API 看重 Vite 工具链,并愿意验证所用功能与部署目标
OpenNext 适配标准 next build 的输出到其他平台 想保留真正的 Next.js 构建结果,同时部署到非 Vercel 环境

这里没有「一定更先进」的排序。Next.js 是 API 定义与行为演进的源头;OpenNext 使用 Next.js 自己的构建输出,因此通常覆盖面更广、非 Vercel 部署经验更成熟;Vinext 选择重做兼容层,以获得 Vite 原生的开发和插件路径,但相应地不应假设每个长尾行为都已经对齐。

特别是缓存组件、部分预渲染、图片与字体处理、原生模块,以及不同平台的运行时差异,都应该列入迁移验收,而不是只看首页能否跑起来。迁移现有应用时,先运行 vinext check,再从一个可回归测试的路由开始;生产流量切换前,要在目标平台跑完整的登录、写入、错误页、缓存失效和流式渲染测试。

本站为何使用 Vinext

这个博客的脚本实际就是:

JSON
{
  "scripts": {
    "dev": "vinext dev",
    "build": "vinext build",
    "start": "vinext start"
  }
}

内容层使用 Vite 的 import.meta.glob 在构建时读取 Markdown;页面层仍按 app/ 路由组织,并借助 RSC 与服务端渲染生成文章页。也就是说,本站不是把 Markdown 先交给一个纯客户端 SPA 再请求回来,而是让内容索引和页面渲染留在服务端边界,再把真正需要交互的部分交给浏览器。

生产环境采用 Node.js + Docker 的运行方式。Cloudflare Workers 是 Vinext 当前最深度集成的目标之一,但这不意味着所有 Vinext 应用都必须部署到 Workers;选择 Node、Workers 或其他适配平台,仍要由你的依赖、网络能力、缓存要求和运维模型决定。

这也是一个很好的提醒:使用 Vinext 的核心收益不是把「Vite」当作部署平台标签,而是选用它的开发/构建模型。部署目标需要单独验证。

什么时候值得选 Vinext

以下情况值得认真评估:

  • 团队熟悉 Vite 插件与调试方式,同时需要文件路由、SSR 或 RSC;
  • 项目已经使用 Next.js 风格的目录与 API,但希望探索 Vite 工具链;
  • 目标是 Cloudflare Workers,或已经有清晰的 Node / Nitro 部署适配方案;
  • 业务有可靠的自动化测试,能验证关键路径和缓存/写入行为。

反过来,下面的选择通常更稳妥:

  • 追求最完整的 Next.js 特性、最早拿到官方新能力:继续使用 Next.js;
  • 重点是把标准 Next.js 部署到其他运行时:先看 OpenNext 或 Next.js 自托管;
  • 完全不需要服务端渲染、RSC、文件路由约定:Vite + React SPA 往往更简单;
  • 应用强依赖尚未验证的 App Router 长尾功能:不要为「换工具」承担生产不确定性。

性能也应按实际项目测量。Vite 的 HMR 和构建速度可能改善开发反馈,但首屏、缓存命中、RSC Payload、数据库耗时、图片与第三方脚本决定了用户体验。工具链的选择应由可测的业务指标和兼容性清单,而不是单个 benchmark 决定。

迁移与新建的最小检查表

  1. 先写下当前使用的 Next.js API:路由、middleware、next/image、字体、缓存、Server Actions、第三方原生模块。
  2. 对现有项目运行 vinext check,把发现的问题变成待验证列表。
  3. 在开发环境检查路由、HMR 与 RSC 边界,在生产构建中检查 SSR、streaming 与 hydration。
  4. 在实际部署平台验证环境变量、Cookie、缓存持久化、图片、日志与错误监控。
  5. 灰度切流前,为登录、支付/写入、权限和内容更新准备回归测试与回滚路径。

Vinext 很适合作为理解现代 React 工程的一面镜子:它让你同时看见 Vite 的模块图、RSC 的多环境构建与 Next.js 风格 API 的边界。但它不该被当成「无成本替换」。把兼容性评估、目标平台验证和真实性能测量放进决策流程,才是使用它的正确姿势。

可继续阅读 Vinext 官方仓库与兼容性说明、Vite RSC 插件说明 与 Next.js 自托管文档。