vinext-vite-nextjs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesevinext — Next.js API on Vite, Deploy Anywhere
vinext — 基于Vite的Next.js API,可部署至任意平台
Skill by ara.so — Daily 2026 Skills collection.
vinext is a Vite plugin that reimplements the Next.js public API surface (routing, SSR, RSC, imports, CLI) so existing Next.js apps run on Vite instead of the Next.js compiler. It targets ~94% API coverage, supports both Pages Router and App Router, and deploys natively to Cloudflare Workers with optional Nitro support for AWS, Netlify, Vercel, and more.
next/*由ara.so提供的Skill — 2026每日技能合集。
vinext是一款Vite插件,重新实现了Next.js的公开API接口(路由、SSR、RSC、导入、CLI),让现有Next.js应用可以基于Vite运行,而非Next.js编译器。它的API覆盖率约达94%,同时支持Pages Router和App Router,可原生部署至Cloudflare Workers,还可通过可选的Nitro支持部署至AWS、Netlify、Vercel等更多平台。
next/*Installation
安装
New project (migrate from Next.js)
新项目(从Next.js迁移)
bash
undefinedbash
undefinedAutomated one-command migration
一键自动化迁移
npx vinext init
This will:
1. Run compatibility check (`vinext check`)
2. Install `vite`, `@vitejs/plugin-react` as devDependencies
3. Install `@vitejs/plugin-rsc`, `react-server-dom-webpack` for App Router
4. Add `"type": "module"` to `package.json`
5. Rename CJS config files (e.g. `postcss.config.js` → `postcss.config.cjs`)
6. Add `dev:vinext` and `build:vinext` scripts
7. Generate a minimal `vite.config.ts`
Migration is **non-destructive** — Next.js still works alongside vinext.npx vinext init
该命令会执行以下操作:
1. 运行兼容性检查(`vinext check`)
2. 安装`vite`、`@vitejs/plugin-react`作为开发依赖
3. 为App Router安装`@vitejs/plugin-rsc`、`react-server-dom-webpack`
4. 向`package.json`中添加`"type": "module"`
5. 重命名CJS配置文件(如`postcss.config.js` → `postcss.config.cjs`)
6. 添加`"dev:vinext"`和`"build:vinext"`脚本
7. 生成极简版`vite.config.ts`
迁移是**无破坏性的** —— Next.js仍可与vinext并行使用。Manual installation
手动安装
bash
npm install -D vinext vite @vitejs/plugin-reactbash
npm install -D vinext vite @vitejs/plugin-reactApp Router only:
仅App Router需要:
npm install -D @vitejs/plugin-rsc react-server-dom-webpack
Update `package.json` scripts:
```json
{
"scripts": {
"dev": "vinext dev",
"build": "vinext build",
"start": "vinext start",
"deploy": "vinext deploy"
}
}npm install -D @vitejs/plugin-rsc react-server-dom-webpack
更新`package.json`中的脚本:
```json
{
"scripts": {
"dev": "vinext dev",
"build": "vinext build",
"start": "vinext start",
"deploy": "vinext deploy"
}
}Agent Skill (AI-assisted migration)
Agent Skill(AI辅助迁移)
bash
npx skills add cloudflare/vinextbash
npx skills add cloudflare/vinextThen in your AI tool: "migrate this project to vinext"
然后在AI工具中输入:"将此项目迁移至vinext"
undefinedundefinedCLI Reference
CLI参考
| Command | Description |
|---|---|
| Start dev server with HMR |
| Production build |
| Local production server for testing |
| Build + deploy to Cloudflare Workers |
| Automated migration from Next.js |
| Scan for compatibility issues before migrating |
| Delegate to eslint or oxlint |
| 命令 | 描述 |
|---|---|
| 启动带HMR的开发服务器 |
| 生产环境构建 |
| 启动本地生产服务器用于测试 |
| 构建并部署至Cloudflare Workers |
| 从Next.js自动迁移 |
| 迁移前扫描兼容性问题 |
| 委托给eslint或oxlint执行代码检查 |
CLI Options
CLI选项
bash
vinext dev -p 3001 -H 0.0.0.0
vinext deploy --preview
vinext deploy --env staging --name my-app
vinext deploy --skip-build --dry-run
vinext deploy --experimental-tpr
vinext init --port 3001 --skip-check --forcebash
vinext dev -p 3001 -H 0.0.0.0
vinext deploy --preview
vinext deploy --env staging --name my-app
vinext deploy --skip-build --dry-run
vinext deploy --experimental-tpr
vinext init --port 3001 --skip-check --forceConfiguration
配置
vinext auto-detects or directory and loads automatically. No is required for basic usage.
app/pages/next.config.jsvite.config.tsvinext会自动检测或目录,并自动加载。基础使用场景下无需。
app/pages/next.config.jsvite.config.tsMinimal vite.config.ts
vite.config.ts极简版vite.config.ts
vite.config.tstypescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
export default defineConfig({
plugins: [
react(),
vinext(),
],
})typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
export default defineConfig({
plugins: [
react(),
vinext(),
],
})App Router vite.config.ts
vite.config.tsApp Router版vite.config.ts
vite.config.tstypescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import rsc from '@vitejs/plugin-rsc'
import { vinext } from 'vinext/vite'
export default defineConfig({
plugins: [
react(),
rsc(),
vinext(),
],
})typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import rsc from '@vitejs/plugin-rsc'
import { vinext } from 'vinext/vite'
export default defineConfig({
plugins: [
react(),
rsc(),
vinext(),
],
})Cloudflare Workers with bindings
带绑定的Cloudflare Workers配置
typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [
cloudflare(),
react(),
vinext(),
],
})typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [
cloudflare(),
react(),
vinext(),
],
})Other platforms via Nitro
基于Nitro部署至其他平台
typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import nitro from 'vite-plugin-nitro'
export default defineConfig({
plugins: [
react(),
vinext(),
nitro({ preset: 'vercel' }), // or 'netlify', 'aws-amplify', 'deno-deploy', etc.
],
})typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import nitro from 'vite-plugin-nitro'
export default defineConfig({
plugins: [
react(),
vinext(),
nitro({ preset: 'vercel' }), // 或 'netlify', 'aws-amplify', 'deno-deploy'等
],
})Project Structure
项目结构
vinext uses the same directory conventions as Next.js — no changes required:
my-app/
├── app/ # App Router (auto-detected)
│ ├── layout.tsx
│ ├── page.tsx
│ └── api/route.ts
├── pages/ # Pages Router (auto-detected)
│ ├── index.tsx
│ └── api/hello.ts
├── public/ # Static assets
├── next.config.js # Loaded automatically
├── package.json
└── vite.config.ts # Optional for basic usagevinext使用与Next.js相同的目录约定 —— 无需修改:
my-app/
├── app/ # App Router(自动检测)
│ ├── layout.tsx
│ ├── page.tsx
│ └── api/route.ts
├── pages/ # Pages Router(自动检测)
│ ├── index.tsx
│ └── api/hello.ts
├── public/ # 静态资源
├── next.config.js # 自动加载
├── package.json
└── vite.config.ts # 基础使用场景下可选Code Examples
代码示例
Pages Router — SSR page
Pages Router — SSR页面
typescript
// pages/index.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
type Props = { data: string }
export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {
return { props: { data: 'Hello from SSR' } }
}
export default function Home({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return <h1>{data}</h1>
}typescript
// pages/index.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
type Props = { data: string }
export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {
return { props: { data: 'Hello from SSR' } }
}
export default function Home({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return <h1>{data}</h1>
}Pages Router — Static generation
Pages Router — 静态生成
typescript
// pages/posts/[id].tsx
import type { GetStaticPaths, GetStaticProps } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false,
}
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
return { props: { id: params?.id } }
}
export default function Post({ id }: { id: string }) {
return <p>Post {id}</p>
}typescript
// pages/posts/[id].tsx
import type { GetStaticPaths, GetStaticProps } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false,
}
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
return { props: { id: params?.id } }
}
export default function Post({ id }: { id: string }) {
return <p>Post {id}</p>
}Pages Router — API route
Pages Router — API路由
typescript
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json({ message: 'Hello from vinext' })
}typescript
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json({ message: 'Hello from vinext' })
}App Router — Server Component
App Router — 服务器组件
typescript
// app/page.tsx
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(r => r.json())
return <main>{data.title}</main>
}typescript
// app/page.tsx
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(r => r.json())
return <main>{data.title}</main>
}App Router — Route Handler
App Router — 路由处理器
typescript
// app/api/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
return NextResponse.json({ status: 'ok' })
}
export async function POST(request: NextRequest) {
const body = await request.json()
return NextResponse.json({ received: body })
}typescript
// app/api/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
return NextResponse.json({ status: 'ok' })
}
export async function POST(request: NextRequest) {
const body = await request.json()
return NextResponse.json({ received: body })
}App Router — Server Action
App Router — 服务器操作
typescript
// app/actions.ts
'use server'
export async function submitForm(formData: FormData) {
const name = formData.get('name')
// server-side logic here
return { success: true, name }
}typescript
// app/form.tsx
'use client'
import { submitForm } from './actions'
export function Form() {
return (
<form action={submitForm}>
<input name="name" />
<button type="submit">Submit</button>
</form>
)
}typescript
// app/actions.ts
'use server'
export async function submitForm(formData: FormData) {
const name = formData.get('name')
// 此处为服务器端逻辑
return { success: true, name }
}typescript
// app/form.tsx
'use client'
import { submitForm } from './actions'
export function Form() {
return (
<form action={submitForm}>
<input name="name" />
<button type="submit">Submit</button>
</form>
)
}Middleware
中间件
typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*'],
}typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*'],
}Cloudflare Workers — Bindings access
Cloudflare Workers — 绑定访问
typescript
// app/api/kv/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getCloudflareContext } from 'cloudflare:workers'
export async function GET(request: NextRequest) {
const { env } = getCloudflareContext()
const value = await env.MY_KV.get('key')
return NextResponse.json({ value })
}typescript
// app/api/kv/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getCloudflareContext } from 'cloudflare:workers'
export async function GET(request: NextRequest) {
const { env } = getCloudflareContext()
const value = await env.MY_KV.get('key')
return NextResponse.json({ value })
}Image optimization
图片优化
typescript
// app/page.tsx
import Image from 'next/image'
export default function Page() {
return (
<Image
src="/hero.png"
alt="Hero"
width={800}
height={400}
priority
/>
)
}typescript
// app/page.tsx
import Image from 'next/image'
export default function Page() {
return (
<Image
src="/hero.png"
alt="Hero"
width={800}
height={400}
priority
/>
)
}Link and navigation
链接与导航
typescript
// app/nav.tsx
'use client'
import Link from 'next/link'
import { useRouter, usePathname } from 'next/navigation'
export function Nav() {
const router = useRouter()
const pathname = usePathname()
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<button onClick={() => router.push('/dashboard')}>Dashboard</button>
</nav>
)
}typescript
// app/nav.tsx
'use client'
import Link from 'next/link'
import { useRouter, usePathname } from 'next/navigation'
export function Nav() {
const router = useRouter()
const pathname = usePathname()
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<button onClick={() => router.push('/dashboard')}>Dashboard</button>
</nav>
)
}Deployment
部署
Cloudflare Workers
Cloudflare Workers
bash
undefinedbash
undefinedAuthenticate (once)
认证(仅需一次)
wrangler login
wrangler login
Deploy
部署
vinext deploy
vinext deploy
Deploy to preview
部署至预览环境
vinext deploy --preview
vinext deploy --preview
Deploy to named environment
部署至指定命名环境
vinext deploy --env production --name my-production-app
For CI/CD, set `CLOUDFLARE_API_TOKEN` environment variable instead of `wrangler login`.vinext deploy --env production --name my-production-app
对于CI/CD,设置`CLOUDFLARE_API_TOKEN`环境变量即可,无需执行`wrangler login`。wrangler.toml
(Cloudflare config)
wrangler.tomlwrangler.toml
(Cloudflare配置)
wrangler.tomltoml
name = "my-app"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"toml
name = "my-app"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"Netlify / Vercel / AWS via Nitro
基于Nitro部署至Netlify / Vercel / AWS
bash
npm install -D vite-plugin-nitrobash
npm install -D vite-plugin-nitroThen add nitro plugin to vite.config.ts with your target preset
然后在vite.config.ts中添加nitro插件并指定目标预设
nitro({ preset: 'netlify' })
nitro({ preset: 'netlify' })
nitro({ preset: 'vercel' })
nitro({ preset: 'vercel' })
nitro({ preset: 'aws-amplify' })
nitro({ preset: 'aws-amplify' })
undefinedundefinednext.config.js
Support
next.config.jsnext.config.js
支持
next.config.jsvinext loads your existing automatically:
next.config.jsjavascript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' },
],
},
env: {
MY_VAR: process.env.MY_VAR,
},
redirects: async () => [
{ source: '/old', destination: '/new', permanent: true },
],
rewrites: async () => [
{ source: '/api/:path*', destination: 'https://backend.example.com/:path*' },
],
}
module.exports = nextConfigvinext会自动加载现有:
next.config.jsjavascript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' },
],
},
env: {
MY_VAR: process.env.MY_VAR,
},
redirects: async () => [
{ source: '/old', destination: '/new', permanent: true },
],
rewrites: async () => [
{ source: '/api/:path*', destination: 'https://backend.example.com/:path*' },
],
}
module.exports = nextConfigCompatibility Check
兼容性检查
Run before migrating to identify unsupported features:
bash
npx vinext checkThis scans for:
- Unsupported options
next.config.js - Deprecated Pages Router APIs
- Experimental Next.js features not yet supported
- CJS config file conflicts
迁移前运行以下命令,识别不支持的功能:
bash
npx vinext check该命令会扫描:
- 不支持的选项
next.config.js - 已废弃的Pages Router API
- 尚未支持的实验性Next.js功能
- CJS配置文件冲突
Common Patterns
常见模式
Environment variables
环境变量
Works the same as Next.js — , , :
.env.env.local.env.productionbash
undefined与Next.js工作方式相同 —— 支持, , :
.env.env.local.env.productionbash
undefined.env.local
.env.local
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=$DATABASE_URL
```typescript
// Accessible in client code (NEXT_PUBLIC_ prefix)
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// Server-only
const dbUrl = process.env.DATABASE_URLNEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=$DATABASE_URL
```typescript
// 客户端代码可访问(带NEXT_PUBLIC_前缀)
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// 仅服务器端可访问
const dbUrl = process.env.DATABASE_URLTypeScript path aliases
TypeScript路径别名
json
// tsconfig.json — works as-is
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}json
// tsconfig.json —— 可直接使用
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}Tailwind CSS
Tailwind CSS
bash
npm install -D tailwindcss postcss autoprefixerbash
npm install -D tailwindcss postcss autoprefixerRename postcss.config.js → postcss.config.cjs (vinext init does this automatically)
重命名postcss.config.js → postcss.config.cjs(vinext init会自动完成此操作)
```javascript
// postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
```javascript
// postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}Troubleshooting
故障排除
ESM conflicts with CJS config files
ESM与CJS配置文件冲突
bash
undefinedbash
undefinedvinext init handles this automatically, or rename manually:
vinext init会自动处理,或手动重命名:
mv postcss.config.js postcss.config.cjs
mv tailwind.config.js tailwind.config.cjs
Ensure `package.json` has `"type": "module"`.mv postcss.config.js postcss.config.cjs
mv tailwind.config.js tailwind.config.cjs
确保`package.json`中包含`"type": "module"`。vinext init
overwrites existing vite.config.ts
vinext initvite.config.tsvinext init
覆盖现有vite.config.ts
vinext initvite.config.tsbash
vinext init --forcebash
vinext init --forceSkip compatibility check during init
迁移初始化时跳过兼容性检查
bash
vinext init --skip-checkbash
vinext init --skip-checkCustom port
自定义端口
bash
vinext dev -p 3001
vinext init --port 3001bash
vinext dev -p 3001
vinext init --port 3001wrangler
not authenticated for deploy
wrangler部署时wrangler
未认证
wranglerbash
wrangler loginbash
wrangler loginor set env var:
或设置环境变量:
export CLOUDFLARE_API_TOKEN=your_token_here
undefinedexport CLOUDFLARE_API_TOKEN=your_token_here
undefinedDry-run deploy to verify config
预演部署以验证配置
bash
vinext deploy --dry-runbash
vinext deploy --dry-runApp Router multi-environment build issues
App Router多环境构建问题
App Router builds produce three environments (RSC + SSR + client). If you see build errors, ensure all three plugins are installed:
bash
npm install -D @vitejs/plugin-rsc react-server-dom-webpackAnd your includes both and plugins in the correct order.
vite.config.tsreact()rsc()App Router构建会生成三个环境(RSC + SSR + 客户端)。如果遇到构建错误,请确保已安装所有三个插件:
bash
npm install -D @vitejs/plugin-rsc react-server-dom-webpack并且中按正确顺序包含和插件。
vite.config.tsreact()rsc()What's Supported (~94% of Next.js API)
支持的功能(约Next.js API的94%)
- ✅ Pages Router (SSR, SSG, ISR, API routes)
- ✅ App Router (RSC, Server Actions, Route Handlers, Layouts, Loading, Error boundaries)
- ✅ Middleware
- ✅ ,
next/image,next/link,next/router,next/navigationnext/head - ✅ ,
next/fontnext/dynamic - ✅ (redirects, rewrites, headers, env, images)
next.config.js - ✅ Cloudflare Workers native deployment with bindings
- ✅ HMR in development
- ✅ TypeScript, Tailwind CSS, CSS Modules
- ⚠️ Experimental Next.js features — lower priority
- ❌ Undocumented Vercel-specific behavior — intentionally not supported
- ✅ Pages Router(SSR、SSG、ISR、API路由)
- ✅ App Router(RSC、服务器操作、路由处理器、布局、加载、错误边界)
- ✅ 中间件
- ✅ ,
next/image,next/link,next/router,next/navigationnext/head - ✅ ,
next/fontnext/dynamic - ✅ (重定向、重写、响应头、环境变量、图片配置)
next.config.js - ✅ 带绑定的Cloudflare Workers原生部署
- ✅ 开发环境下的HMR
- ✅ TypeScript、Tailwind CSS、CSS Modules
- ⚠️ 实验性Next.js功能 —— 优先级较低
- ❌ 未公开的Vercel特定行为 —— 有意不支持