Skip to content

Vinext 1.0 发布:把 Next.js 搬到 Vite 上,迁移前该检查什么 ​

译注:本文编译自 dev.to 文章《Next.js on Vite With Vinext 1.0: What I'd Check Before Migrating》,作者 Abrar Qasim。原文基于 Cloudflare 的 Vinext 1.0 发布公告,以开发者视角梳理了迁移前需要自行验证的要点。原文链接见文末。

一句话结论:Vinext 1.0 已经好到值得这周在副业项目上试一试,但还没好到能不经几天验证就把客户的生产应用迁过去。

先坦白一点:作者本人尚未把真实应用迁移到 Vinext。这篇文章是他拿着 Cloudflare 的 1.0 公告逐条核对,把「能自己验证的声明」和「只能选择相信的声明」分开,并写下自己会在项目上跑的测试。这比写一篇基准测试文章工作量小,但他宁愿诚实地做一件小事,也不愿编造一段迁移故事。

为什么值得关注?作者接的客户项目大多基于 Next.js,其中一些跑在便宜的 VPS 上——对客户来说,托管账单比框架偏好的平台更重要。任何能让 Next.js 应用更具可移植性的东西,迟早会摆到他桌上。

Next.js vs Vite 是个伪命题 ​

搜索「nextjs vs vite」会看到大量把两者当作竞争对手的对比。它们并不是。Next.js 是框架:路由、渲染模式、缓存、Server Actions。Vite 是构建工具和开发服务器。你可以在 Vite 之上构建路由和渲染器,很多框架就是这么做的,但 Next.js 不是其中之一——它自带打包器。

所以当有人问 Next.js 是否使用 Vite,答案是否定的。Vinext 做的事比一张对比表更有意思:它重新实现了公开的 next/* 接口,让你现有的 Next.js 代码改走 Vite 的流水线。Cloudflare 团队称,该项目始于今年 2 月,最初是一位工程师为期一周、由 AI 驱动的实验,七个月后达到 1.0,已有客户在生产环境运行高流量的动态应用。

据公告,它支持 App Router、Pages Router 以及混合项目,涵盖 React Server Components、Server Actions、API 路由、路由处理器、中间件和客户端导航。可部署到 Cloudflare Workers(含免费计划)、Netlify、AWS Lambda 或普通 Node。Pages Router 的支持让作者意外——他本以为这类项目会追逐光鲜的 App Router 特性,而忽略那些难以迁移的大型老应用,而那些老应用恰恰是他被雇来抢救的对象。

迁移路径只需两条命令:

shell
# 检查项目是否兼容
npx vinext check

# 配置 Vite 和部署配置,保留 Next.js 结构
npx vinext init

注意第二条命令的承诺:项目结构保持不变。这是个好迹象,意味着一次失败的实验只需 git checkout . 就能回退。

99% 兼容性数字说明了什么,又没说明什么 ​

核心声明是:客户最关注的功能的测试兼容性现已超过 99%,不含缓存组件。团队每晚还会针对 Vinext 运行 Next.js 端到端测试套件,并在两个路由、开发与生产服务器、Node.js 与 Workers 目标上维护数千个自有测试。

作者认可这一点:每晚跑上游套件,能在用户之前发现偏移。但有两个从外部无法回答的问题。

第一,99% 是什么的 99%?这个数字覆盖的是 Cloudflare 挑选出的、对客户重要的功能。你应用里最奇怪的中间件不在这个集合里。第二,数字来自厂商。这不代表它错,但作者会像对待任何厂商基准一样对待它——作为自己跑测试的理由,而不是结论。

公告中有一句话让作者反复回味:作者说,构建一个替代的 revalidatePath 很简单,难的是确保它真正影响已渲染页面、缓存条目和后续请求。这正是作者会直接测试的东西:

typescript
'use server'

import { revalidatePath } from 'next/cache'
import { db } from '@/lib/db'

export async function publishPost(id: string) {
  await db.post.update({ where: { id }, data: { published: true } })
  // 部署后,这真的会刷新 /blog 和 /blog/[slug] 吗?
  revalidatePath('/blog')
  revalidatePath(`/blog/${id}`)
}

计划很朴素:部署到预览环境,调用该 action,然后用 curl -I 请求页面两次,比较缓存头和响应体。如果 action 返回后页面仍是旧的,就找到了通过率所掩盖的缺口。

缓存预热:即使不用 Vinext 也值得借鉴 ​

如果站点有长尾 URL,构建时预渲染就变成排队:调用 generateStaticParams() 或 getStaticPaths(),构建机一个接一个渲染数万页面,其中大多数几乎没有流量。与此同时,真正重要的少数页面一小时前就渲染完了,你还在等。

Vinext 的答案是把这部分工作移出构建机:新 Worker 版本先以 0% 生产流量发布,流水线从该版本请求选定页面填充缓存,预热完成后再提升版本。发布日真实用户不会碰到冷页面。你也可以让 Vinext 在 Next.js 代码声明之外,自动加入高流量页面。

命令很短:

shell
npx @vinext/cloudflare deploy --warm-cache

对比过去限制构建时间的老办法:

tsx
// 以前:构建时只渲染一部分,指望其余按需渲染足够便宜
export async function generateStaticParams() {
  const top = await getTopPosts(50)
  return top.map((p) => ({ slug: p.slug }))
}
export const dynamicParams = true

这能行,但你在猜哪 50 个重要,其他页面的第一个访客要承担渲染成本。在构建后、流量切换前预热缓存,消除了这种猜测。

但有个真实的注意事项:公告是以 Cloudflare 网络和 Workers 部署流程来描述预热的。作者不知道有多少能迁移到 Netlify 或 Lambda,公告也没说。如果你考虑 Vinext 是为了可移植性,请检查你想要的功能是否也可移植。

use cache 的缺口 ​

公告中最诚实的一段关于 Cache Components。Next.js 16 将其作为重要卖点,而 Vinext 目前对 "use cache" 指令只有有限支持。Cloudflare 称,他们接触的大多数团队并未使用 Cache Components,也不把支持它当作迁移前提。

作者相信这是他们客户的情况,但这不能说明你的情况。愿意尝试 Next.js 在 Vite 上重实现的团队本身就是自我选择的群体。如果你的应用已经依赖 "use cache",第一件事是弄清依赖程度:

shell
grep -rn '"use cache"' app src --include='*.ts' --include='*.tsx' | wc -l

零最好。十几个意味着你该对照 Next.js 文档和 vinext.dev 上的兼容性矩阵,逐路由决定。作者不是说这个缺口是致命伤,而是说这是公告中唯一明确告诉你「数字到此为止」的地方。

还有第二个更慢的风险:Next.js canary 每天都有新 commit,所以每天早上有 agent 审查 diff 并为可能影响 Vinext 的改动开追踪 issue,每晚重新生成兼容性矩阵。测试发现缺口时,agent 复现并提出修复,人类维护者处理需要判断如何把 Next.js 映射到 Vite 的情况。这套设置很聪明,也说明项目在设计上就在追一个移动的目标。只要 Cloudflare 出资就没问题,但这是作者对任何兼容层都会问的「巴士系数」问题:如果赞助方失去兴趣,谁来跟进上游?代码在 github.com/cloudflare/vinext 开源,至少可以自己看提交节奏。

动客户应用前会跑什么 ​

作者的清单,按执行顺序,都不原创,除命令外也不来自公告。

从分支开始,运行 npx vinext check,把输出当作待办清单并数条目。如果数量少,运行 npx vinext init,看开发服务器能否启动。然后对两个构建跑现有端到端测试。如果没有,写五个:登录、触发 Server Action 的表单、依赖 revalidatePath 的页面、中间件重定向、一个图片密集页面。五个测试真实行为的用例,比任何通过率都更能说明问题。

之后用简单方式比较两个构建的响应:

shell
for path in / /blog /blog/some-post /api/health; do
  echo "== $path"
  diff <(curl -sI "https://old.example.com$path" | sort) \
       <(curl -sI "https://new.example.com$path" | sort)
done

缓存路由的响应头差异是首要查看点,因为重实现最可能在这里与原版不一致。公告称 Vinext 的追踪可与现有 OpenTelemetry 和 Sentry 设置配合,如果你用其中之一,故意触发一个错误,确认它以合理的 trace 出现。作者更信任一个自己见过正确失败的东西,而不是只见过成功的东西。

到底该不该迁移 ​

作者诚实的判断按情况划分:如果你困在 Pages Router 的大型应用上,且部署目标不是 Next.js 喜欢的平台,Vinext 是他见过的第一个把你当一等公民的项目,值得花一天试试。如果你想把 Next.js 应用放到 Workers 上并做缓存预热,答案相同。如果你依赖 Cache Components,或依赖作者未列出的平台特定 Next.js 特性,那就等待并观察兼容性矩阵。

如果你对现状满意,就留在原地。一个能工作的部署比更快的开发服务器更有价值——这话出自一个喜欢快速开发服务器的人。

这周可以做的事:挑一个你最不重要的 Next.js 项目,建分支,运行 npx vinext check,把输出贴进 issue,数不支持项,grep "use cache"。这是一小时的工作,结束后你就知道更大的迁移是否值得规划,还是可以关掉标签页。

原文链接 ​