关键代码验证
技术研究 人工智能 GitHub
mkdir my-vinext-app && cd my-vinext-app
快速开始示例
全新项目创建
# 创建项目目录
mkdir my-vinext-app && cd my-vinext-app
# 初始化 package.json
npm init -y
# 安装依赖
npm install vinext next react react-dom
npm install -D vite @vitejs/plugin-rsc
# 对于 Cloudflare Workers 部署
npm install -D @cloudflare/vite-plugin wrangler
基础配置
App Router 配置 (vite.config.ts)
import { defineConfig } from "vite";
import vinext from "vinext";
import rsc from "@vitejs/plugin-rsc";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [
// 1. Vinext 核心插件 - 处理 Next.js API 兼容
vinext(),
// 2. RSC 插件 - 支持 React Server Components
rsc({
entries: {
// RSC 服务端渲染入口
rsc: "virtual:vinext-rsc-entry",
// SSR 入口
ssr: "virtual:vinext-app-ssr-entry",
// 客户端水合入口
client: "virtual:vinext-app-browser-entry",
},
}),
// 3. Cloudflare 插件 - Workers 运行时支持
cloudflare({
viteEnvironment: {
name: "rsc",
childEnvironments: ["ssr"]
},
}),
],
});
Pages Router 配置(更简单)
import { defineConfig } from "vite";
import vinext from "vinext";
export default defineConfig({
plugins: [vinext()],
});
项目结构
my-vinext-app/
├── app/ # App Router 目录
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 首页
│ ├── loading.tsx # 加载状态
│ ├── error.tsx # 错误处理
│ └── about/
│ └── page.tsx # /about 页面
├── public/ # 静态资源
├── next.config.js # Next.js 配置(兼容)
├── vite.config.ts # Vite 配置
└── package.json
核心功能验证
1. Server Components 示例
// app/page.tsx - Server Component(默认)
async function getData() {
const res = await fetch('https://api.example.com/data', {
// 自动使用 'use cache' 或 ISR
next: { revalidate: 60 }
});
return res.json();
}
export default async function HomePage() {
// 直接在服务端获取数据
const data = await getData();
return (
<main>
<h1>Server Component 示例</h1>
<p>数据获取时间: {new Date().toISOString()}</p>
<pre>{JSON.stringify(data, null, 2)}</pre>
</main>
);
}
2. Client Components 示例
// app/counter.tsx - Client Component
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
// app/page.tsx - 在 Server Component 中使用 Client Component
import { Counter } from './counter';
export default function HomePage() {
return (
<main>
<h1>混合渲染示例</h1>
<p>这部分在服务端渲染</p>
<Counter /> {/* 客户端水合 */}
</main>
);
}
3. Server Actions 示例
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createTodo(formData: FormData) {
const title = formData.get('title') as string;
// 在服务端执行数据库操作
await db.todo.create({ data: { title } });
// 重新验证缓存
revalidatePath('/todos');
// 或重定向
redirect('/todos');
}
// app/todos/page.tsx
import { createTodo } from '../actions';
export default function TodosPage() {
return (
<form action={createTodo}>
<input name="title" placeholder="New todo" required />
<button type="submit">Add</button>
</form>
);
}
4. 缓存配置示例
// 使用 Cloudflare KV 作为缓存后端
import { KVCacheHandler } from 'vinext/cloudflare';
import { setCacheHandler } from 'next/cache';
// 在 worker/index.ts 或入口文件中设置
export default {
async fetch(request: Request, env: Env) {
// 设置 KV 缓存处理器
setCacheHandler(new KVCacheHandler(env.CACHE_KV));
// ... 处理请求
}
};
// 使用 "use cache" 指令
'use cache';
import { cacheLife, cacheTag } from 'next/cache';
// 函数级别的缓存
export async function getCachedData() {
'use cache';
cacheLife('hours'); // 缓存 1 小时
cacheTag('data'); // 可标记清除
return fetchExpensiveData();
}
5. 中间件示例
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 获取请求路径
const { pathname } = request.nextUrl;
// 检查认证
const token = request.cookies.get('token')?.value;
// 保护 /admin 路由
if (pathname.startsWith('/admin') && !token) {
return NextResponse.redirect(new URL('/login', request.url));
}
// 添加自定义 header
const response = NextResponse.next();
response.headers.set('x-custom-header', 'vinext');
return response;
}
export const config = {
matcher: ['/((?!_next/static|favicon.ico).*)'],
};
从现有 Next.js 项目迁移
自动化迁移
# 在现有 Next.js 项目中
npx vinext init
该命令会自动:
- 运行兼容性检查 (
vinext check) - 安装 Vite 和相关依赖
- 重命名 CJS 配置文件(避免 ESM 冲突)
- 添加
"type": "module"到 package.json - 添加 npm scripts
- 生成
vite.config.ts
手动迁移步骤
- 安装依赖
npm install vinext
npm install -D vite @vitejs/plugin-rsc @cloudflare/vite-plugin
- 更新 package.json scripts
{
"scripts": {
"dev:vinext": "vinext dev",
"build:vinext": "vinext build",
"deploy:vinext": "vinext deploy",
"dev": "next dev",
"build": "next build"
}
}
-
创建 vite.config.ts(见上方配置示例)
-
处理兼容性问题
# 检查兼容性
npx vinext check
# 输出示例:
# ✓ next/link - 支持
# ✓ App Router - 支持
# ⚠ next/image - 部分支持(无构建时优化)
# ✗ sharp - Workers 环境不支持(自动 stub)
常见迁移问题及解决方案
| 问题 | 解决方案 |
|---|---|
| CJS 配置报错 | 重命名为 .cjs 后缀 |
| path alias 不生效 | 安装 vite-tsconfig-paths |
| MDX 不支持 | 添加 @mdx-js/rollup |
| 原生 Node 模块报错 | 使用 Vinext 自动 stub 或手动 polyfill |
| 图片优化失效 | 使用 @unpic/react 或 Cloudflare Images |
部署到 Cloudflare Workers
配置 wrangler
// wrangler.jsonc
{
"name": "my-vinext-app",
"compatibility_date": "2026-02-26",
"main": "./worker/index.ts",
"assets": {
"directory": "./dist/client"
},
"kv_namespaces": [
{
"binding": "CACHE_KV",
"id": "your-kv-namespace-id"
}
],
"d1_databases": [
{
"binding": "DB",
"database_id": "your-d1-database-id"
}
]
}
部署命令
# 一键部署
vinext deploy
# 指定环境
vinext deploy --env staging
# 预览部署
vinext deploy --preview
# 带 TPR(流量感知预渲染)
vinext deploy --experimental-tpr
Worker 入口文件示例
// worker/index.ts
import { createVinextHandler } from 'vinext/cloudflare';
export default createVinextHandler({
// 自定义缓存配置
cacheHandler: env => new KVCacheHandler(env.CACHE_KV),
// 错误处理
onError: (error, request) => {
console.error('Request failed:', request.url, error);
},
});
性能优化配置
1. 图片优化
// 使用 @unpic/react 替代 next/image
import { Image } from '@unpic/react';
export function OptimizedImage() {
return (
<Image
src="https://example.com/photo.jpg"
layout="constrained"
width={800}
height={600}
alt="Description"
/>
);
}
2. 字体加载
// 使用 next/font/google(运行时加载)
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function Layout({ children }) {
return (
<html className={inter.className}>
<body>{children}</body>
</html>
);
}
3. 代码分割
Vite 自动处理代码分割,可通过动态导入进一步优化:
// 动态导入 heavy component
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <p>Loading chart...</p>,
ssr: false, // 禁用服务端渲染
});
参考资料
- Vinext Examples - 官方示例集合
- Cloudflare Workers Documentation - Workers 文档
- Vite Configuration Reference - Vite 配置参考