Loading...
Loading...
Compare original and translation side by side
| Factor | Questions to Ask |
|---|---|
| Team Size | Solo dev → simpler stack; Team → tooling/types matter |
| Timeline | MVP → batteries-included; Long-term → flexibility |
| Deployment | Cloudflare → Next.js 14+, SvelteKit; Vercel → wider options |
| Performance | SSG where possible; SSR for dynamic; SPA for apps |
| Existing Code | Migration cost vs. rewrite; incremental adoption paths |
| 因素 | 调研问题 |
|---|---|
| 团队规模 | 独立开发者 → 更简洁的技术栈;团队协作 → 工具类型与类型安全至关重要 |
| 项目周期 | MVP阶段 → 开箱即用型技术栈;长期项目 → 高灵活性技术栈 |
| 部署目标 | Cloudflare → Next.js 14+、SvelteKit;Vercel → 更多可选方案 |
| 性能要求 | 尽可能使用SSG;动态内容使用SSR;应用类项目使用SPA |
| 现有代码 | 迁移成本与重写的对比;渐进式 adoption 路径 |
const stackRecommendations = {
// Marketing sites
marketingSite: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS",
components: "shadcn/ui",
deployment: "Cloudflare Pages",
rationale: "SSG for speed, great DX, edge deployment"
},
// Internal tools
internalTools: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS",
components: "shadcn/ui + react-hook-form + zod",
auth: "Cloudflare Access",
deployment: "Cloudflare Pages (with Access protection)",
rationale: "Fast iteration, zero-config auth, preview URLs"
},
// Interactive gallery/portfolio
gallery: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS + Framer Motion",
components: "shadcn/ui + custom",
images: "next/image + Pexels/Unsplash API",
deployment: "Cloudflare Pages",
rationale: "Optimized images, smooth animations, edge CDN"
},
// E-commerce
ecommerce: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS",
components: "shadcn/ui + Stripe Elements",
payments: "Stripe",
deployment: "Vercel (better Next.js support) or Cloudflare",
rationale: "SSR for SEO, edge caching, Stripe integration"
}
};const stackRecommendations = {
// 营销站点
marketingSite: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS",
components: "shadcn/ui",
deployment: "Cloudflare Pages",
rationale: "SSG保障速度,优秀的开发体验,边缘部署"
},
// 内部工具
internalTools: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS",
components: "shadcn/ui + react-hook-form + zod",
auth: "Cloudflare Access",
deployment: "Cloudflare Pages (with Access protection)",
rationale: "快速迭代,零配置认证,预览URL"
},
// 交互式画廊/作品集
gallery: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS + Framer Motion",
components: "shadcn/ui + custom",
images: "next/image + Pexels/Unsplash API",
deployment: "Cloudflare Pages",
rationale: "优化图片加载,流畅动画,边缘CDN"
},
// 电商平台
ecommerce: {
framework: "Next.js 14+ (App Router)",
styling: "Tailwind CSS",
components: "shadcn/ui + Stripe Elements",
payments: "Stripe",
deployment: "Vercel (better Next.js support) or Cloudflare",
rationale: "SSR利于SEO,边缘缓存,Stripe集成"
}
};undefinedundefinedundefinedundefined| Environment | Trigger | URL Pattern |
|---|---|---|
| Preview | PR opened/updated | |
| Staging | Push to | |
| Production | Push to | |
| 环境 | 触发条件 | URL规则 |
|---|---|---|
| 预览环境 | PR创建/更新 | |
| 预发布环境 | 推送到 | |
| 生产环境 | 推送到 | |
# Every PR gets a unique URL
npx wrangler pages deploy out --project-name=your-project
# → https://preview-feature-123.your-project.pages.dev// middleware.ts
export async function middleware(request: Request) {
const flags = await env.KV.get('feature-flags', 'json');
if (flags?.newCheckout && request.url.includes('/checkout')) {
return NextResponse.rewrite(new URL('/checkout-v2', request.url));
}
}# Access policy (configure in Cloudflare dashboard)
Application: internal-tools.example.com
Policy: Allow authenticated users from @company.com# 每个PR都会生成唯一URL
npx wrangler pages deploy out --project-name=your-project
# → https://preview-feature-123.your-project.pages.dev// middleware.ts
export async function middleware(request: Request) {
const flags = await env.KV.get('feature-flags', 'json');
if (flags?.newCheckout && request.url.includes('/checkout')) {
return NextResponse.rewrite(new URL('/checkout-v2', request.url));
}
}# 访问策略(在Cloudflare控制台配置)
Application: internal-tools.example.com
Policy: Allow authenticated users from @company.com| Component Need | Recommendation |
|---|---|
| Basic UI (Button, Input, Dialog) | shadcn/ui - copy-paste, customize |
| Complex forms | shadcn/ui Form + react-hook-form + zod |
| Data tables | shadcn/ui Table + TanStack Table |
| Date picking | shadcn/ui Calendar + date-fns |
| Charts | Recharts (shadcn has examples) |
| Drag & drop | dnd-kit (not bundled, but compatible) |
| 组件需求 | 推荐方案 |
|---|---|
| 基础UI(按钮、输入框、弹窗) | shadcn/ui - 直接复制粘贴并自定义 |
| 复杂表单 | shadcn/ui Form + react-hook-form + zod |
| 数据表格 | shadcn/ui Table + TanStack Table |
| 日期选择 | shadcn/ui Calendar + date-fns |
| 图表 | Recharts(shadcn有示例) |
| 拖拽功能 | dnd-kit(未内置,但兼容) |
// components/ui/button.tsx - shadcn baseline
import { cn } from "@/lib/utils";
import { buttonVariants } from "./button-variants";
// Extend with your design tokens
export const Button = ({ className, variant, size, ...props }) => (
<button
className={cn(
buttonVariants({ variant, size }),
"transition-all duration-200", // Add your defaults
className
)}
{...props}
/>
);// components/ui/button.tsx - shadcn基础模板
import { cn } from "@/lib/utils";
import { buttonVariants } from "./button-variants";
// 结合设计令牌扩展组件
export const Button = ({ className, variant, size, ...props }) => (
<button
className={cn(
buttonVariants({ variant, size }),
"transition-all duration-200", // 添加自定义默认样式
className
)}
{...props}
/>
);internal.yourapp.com/
├── Cloudflare Access (SSO protection)
│ └── Policy: Allow @company.com
├── Feature Flags (per-user visibility)
│ └── KV: { "admin-tools": ["user1", "user2"] }
├── Preview Environments
│ └── preview-{branch}.internal.yourapp.com
└── Routes
├── /admin → Full admin dashboard
├── /beta → Beta feature preview
└── /debug → Developer toolsinternal.yourapp.com/
├── Cloudflare Access(SSO保护)
│ └── 策略:允许@company.com域名用户访问
├── 特性开关(按用户可见)
│ └── KV: { "admin-tools": ["user1", "user2"] }
├── 预览环境
│ └── preview-{branch}.internal.yourapp.com
└── 路由
├── /admin → 完整管理仪表盘
├── /beta → 新特性预览
└── /debug → 开发者工具// middleware.ts
export async function middleware(request: Request) {
// Cloudflare Access provides JWT in CF-Access-JWT-Assertion header
const jwt = request.headers.get('CF-Access-JWT-Assertion');
const user = await verifyAccessToken(jwt);
const flags = await env.KV.get(`user:${user.email}:flags`, 'json');
if (request.url.includes('/admin') && !flags?.admin) {
return new Response('Forbidden', { status: 403 });
}
return NextResponse.next();
}// middleware.ts
export async function middleware(request: Request) {
// Cloudflare Access在CF-Access-JWT-Assertion请求头中提供JWT
const jwt = request.headers.get('CF-Access-JWT-Assertion');
const user = await verifyAccessToken(jwt);
const flags = await env.KV.get(`user:${user.email}:flags`, 'json');
if (request.url.includes('/admin') && !flags?.admin) {
return new Response('Forbidden', { status: 403 });
}
return NextResponse.next();
}// lib/design-bridge.ts
import { buttonPatterns } from '@/data/catalog/button-patterns.json';
// Map catalog patterns to shadcn variants
export const variantMap = {
'primary-button': 'default',
'secondary-button': 'outline',
'destructive-button': 'destructive',
'tertiary-button': 'ghost',
'neobrutalism-button': 'brutalist', // custom variant
} as const;
// Generate Tailwind classes from catalog specs
export function patternToClasses(patternId: string): string {
const pattern = buttonPatterns.find(p => p.id === patternId);
if (!pattern) return '';
return cn(
pattern.cssProperties.map(prop => propertyToTailwind(prop)),
pattern.variants?.hover && 'hover:' + pattern.variants.hover
);
}// lib/design-bridge.ts
import { buttonPatterns } from '@/data/catalog/button-patterns.json';
// 将设计规范映射到shadcn变体
export const variantMap = {
'primary-button': 'default',
'secondary-button': 'outline',
'destructive-button': 'destructive',
'tertiary-button': 'ghost',
'neobrutalism-button': 'brutalist', // 自定义变体
} as const;
// 根据设计规范生成Tailwind类名
export function patternToClasses(patternId: string): string {
const pattern = buttonPatterns.find(p => p.id === patternId);
if (!pattern) return '';
return cn(
pattern.cssProperties.map(prop => propertyToTailwind(prop)),
pattern.variants?.hover && 'hover:' + pattern.variants.hover
);
}undefinedundefined| Pro | Con |
|---|---|
| [Benefit] | [Drawback] |
| 优势 | 劣势 |
|---|---|
| [优点] | [缺点] |
undefinedundefinedreferences/stack-decisions.mdreferences/cloudflare-patterns.mdreferences/shadcn-components.mdreferences/internal-tools.mdreferences/design-system-bridge.mdreferences/stack-decisions.mdreferences/cloudflare-patterns.mdreferences/shadcn-components.mdreferences/internal-tools.mdreferences/design-system-bridge.md