Skip to main content

🌍 出海站实战:从0到上线

这一章,我们不讲理论——直接动手做一个能赚钱的海外工具站。 从选项目到部署上线,全程2小时,跟着做就能出结果。


🎯 为什么选「AI Prompt Generator」?

在决定做出海站之前,我用 Ahrefs 做了大量关键词调研。最终锁定「AI Prompt Generator」这个方向,原因很简单:

维度数据判断
月搜索量18,100✅ 足够大
KD(关键词难度)12✅ 新站也能排上去
CPC(点击成本)$1.20✅ 广告主愿意花钱
内容生成难度✅ AI可以批量产出
变现路径AdSense + 联盟✅ 清晰可行

其他候选方向(供参考):

  • ❌ AI Image Generator — KD太高(45+),新站排不上去
  • ❌ ChatGPT Alternatives — 大站霸榜,竞争激烈
  • ✅ AI Prompt Generator — 甜蜜点:需求大 + 竞争低 + 内容可批量

📋 PRD:产品需求文档

目标用户

  • 主要用户:英语圈的 AI 爱好者、内容创作者、开发者
  • 典型场景:想用 ChatGPT/Claude 但不知道怎么写 Prompt
  • 痛点:网上 Prompt 太散、质量参差不齐、找不到适合自己场景的

核心功能

用户输入需求 → 选择场景(写作/编程/营销/...) → 生成高质量 Prompt → 复制使用

技术栈选择

层级技术理由
框架Next.js 14 (App Router)SSR/SSG 对 SEO 友好
样式Tailwind CSS快速出页面,不用写 CSS
部署Vercel免费、自动部署、全球CDN
字体Inter海外站标配,可读性好
图标Lucide React轻量、风格统一

页面结构

/ → 首页(Hero + 分类入口 + 热门Prompt)
/generate → Prompt 生成器(核心功能页)
/templates → Prompt 模板库
/templates/[slug] → 单个模板详情页(程序化SEO核心)
/about → 关于我们
/privacy → 隐私政策
/terms → 服务条款

🛠️ 用 Codex 开发:完整对话过程

第一轮:项目初始化

我的 Prompt:

Create a Next.js 14 project with App Router and Tailwind CSS for an AI Prompt Generator tool site. The site should have:

  1. A homepage with hero section, category grid, and popular prompts
  2. A prompt generation page where users input requirements and get prompts
  3. A template library page
  4. Proper SEO meta tags
  5. Responsive design

Use TypeScript, Inter font, and Lucide React for icons.

Codex 的响应:

# Codex 自动执行了以下命令
npx create-next-app@latest ai-prompt-generator \
--typescript --tailwind --eslint --app --src-dir \
--import-alias "@/*"

cd ai-prompt-generator
npm install lucide-react

然后生成了完整的项目结构:

ai-prompt-generator/
├── src/
│ ├── app/
│ │ ├── layout.tsx # 根布局
│ │ ├── page.tsx # 首页
│ │ ├── generate/
│ │ │ └── page.tsx # 生成器页面
│ │ ├── templates/
│ │ │ ├── page.tsx # 模板库
│ │ │ └── [slug]/
│ │ │ └── page.tsx # 模板详情
│ │ ├── about/
│ │ │ └── page.tsx
│ │ ├── privacy/
│ │ │ └── page.tsx
│ │ └── terms/
│ │ └── page.tsx
│ ├── components/
│ │ ├── Hero.tsx
│ │ ├── CategoryGrid.tsx
│ │ ├── PromptGenerator.tsx
│ │ ├── PromptResult.tsx
│ │ ├── TemplateCard.tsx
│ │ ├── Header.tsx
│ │ └── Footer.tsx
│ └── lib/
│ ├── prompts.ts # Prompt数据
│ └── categories.ts # 分类数据
├── public/
│ └── favicon.ico
├── tailwind.config.ts
└── package.json

第二轮:首页开发

我的 Prompt:

Build the homepage. Requirements:

  • Hero section: "Generate Perfect AI Prompts in Seconds" with a search input and CTA button
  • Category grid: 6 categories (Writing, Coding, Marketing, Business, Education, Creative) with icons
  • Popular prompts section: show 8 prompt templates in a 2x4 grid
  • Footer with links
  • Make it look clean, modern, professional — like a SaaS landing page

Codex 生成的关键代码:

首页 src/app/page.tsx

import { Hero } from '@/components/Hero'
import { CategoryGrid } from '@/components/CategoryGrid'
import { PopularPrompts } from '@/components/PopularPrompts'

export default function Home() {
return (
<main className="min-h-screen">
<Hero />
<CategoryGrid />
<PopularPrompts />
</main>
)
}

Hero 组件 src/components/Hero.tsx

'use client'

import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Sparkles, ArrowRight } from 'lucide-react'

export function Hero() {
const [query, setQuery] = useState('')
const router = useRouter()

const handleGenerate = () => {
if (query.trim()) {
router.push(`/generate?q=${encodeURIComponent(query)}`)
}
}

return (
<section className="relative overflow-hidden bg-gradient-to-b from-violet-50 to-white">
<div className="mx-auto max-w-7xl px-4 py-24 sm:px-6 lg:px-8">
<div className="text-center">
<div className="mb-6 inline-flex items-center rounded-full bg-violet-100 px-4 py-2 text-sm font-medium text-violet-700">
<Sparkles className="mr-2 h-4 w-4" />
Free AI Prompt Generator
</div>
<h1 className="text-5xl font-bold tracking-tight text-gray-900 sm:text-6xl">
Generate Perfect AI Prompts
<br />
<span className="text-violet-600">in Seconds</span>
</h1>
<p className="mx-auto mt-6 max-w-2xl text-lg text-gray-600">
Stop guessing. Get optimized prompts for ChatGPT, Claude, and Gemini.
Choose from 500+ templates or generate custom ones.
</p>
<div className="mx-auto mt-10 flex max-w-md gap-3">
<input
type="text"
placeholder="Describe what you want to achieve..."
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleGenerate()}
className="flex-1 rounded-xl border border-gray-300 px-4 py-3 text-sm shadow-sm focus:border-violet-500 focus:outline-none focus:ring-2 focus:ring-violet-500/20"
/>
<button
onClick={handleGenerate}
className="inline-flex items-center rounded-xl bg-violet-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-violet-700 transition-colors"
>
Generate
<ArrowRight className="ml-2 h-4 w-4" />
</button>
</div>
</div>
</div>
</section>
)
}

分类网格 src/components/CategoryGrid.tsx

import {
PenLine, Code2, Megaphone, Briefcase, GraduationCap, Palette
} from 'lucide-react'

const categories = [
{ name: 'Writing', icon: PenLine, count: 120, color: 'bg-blue-50 text-blue-600' },
{ name: 'Coding', icon: Code2, count: 95, color: 'bg-green-50 text-green-600' },
{ name: 'Marketing', icon: Megaphone, count: 80, color: 'bg-orange-50 text-orange-600' },
{ name: 'Business', icon: Briefcase, count: 65, color: 'bg-purple-50 text-purple-600' },
{ name: 'Education', icon: GraduationCap, count: 55, color: 'bg-red-50 text-red-600' },
{ name: 'Creative', icon: Palette, count: 70, color: 'bg-pink-50 text-pink-600' },
]

export function CategoryGrid() {
return (
<section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
<h2 className="text-center text-3xl font-bold text-gray-900">
Browse by Category
</h2>
<p className="mx-auto mt-3 max-w-2xl text-center text-gray-600">
Find the perfect prompt for your specific use case
</p>
<div className="mt-12 grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6">
{categories.map((cat) => (
<a
key={cat.name}
href={`/templates?category=${cat.name.toLowerCase()}`}
className="group flex flex-col items-center rounded-2xl border border-gray-200 p-6 transition-all hover:border-violet-300 hover:shadow-lg"
>
<div className={`rounded-xl p-3 ${cat.color}`}>
<cat.icon className="h-6 w-6" />
</div>
<h3 className="mt-4 font-semibold text-gray-900">{cat.name}</h3>
<p className="mt-1 text-sm text-gray-500">{cat.count} prompts</p>
</a>
))}
</div>
</section>
)
}

第三轮:Prompt 生成器组件

我的 Prompt:

Build the PromptGenerator component for the /generate page:

  • A form with: category select, tone select (professional/casual/creative), detail level slider
  • A textarea for the user's requirement description
  • A "Generate Prompt" button
  • Show a loading state while generating
  • Display the result in a nice card with copy-to-clipboard button
  • Use a mock generation function (we'll add real API later)
  • Make it all responsive

Codex 生成的生成器组件:

'use client'

import { useState } from 'react'
import { Copy, Check, Loader2, Sparkles } from 'lucide-react'

const categories = [
'Writing', 'Coding', 'Marketing', 'Business', 'Education', 'Creative'
]

const tones = [
{ value: 'professional', label: 'Professional' },
{ value: 'casual', label: 'Casual' },
{ value: 'creative', label: 'Creative' },
{ value: 'technical', label: 'Technical' },
]

function generateMockPrompt(
requirement: string,
category: string,
tone: string,
detail: number
): string {
const roleMap: Record<string, string> = {
Writing: 'an expert content writer with 10 years of experience',
Coding: 'a senior software engineer specializing in clean, maintainable code',
Marketing: 'a digital marketing strategist with expertise in growth hacking',
Business: 'a business consultant who has helped 100+ startups scale',
Education: 'an experienced educator and curriculum designer',
Creative: 'a creative director with a background in storytelling',
}

const role = roleMap[category] || 'an expert'
const detailInstructions = detail > 7
? 'Be extremely detailed, provide examples, and cover edge cases.'
: detail > 4
? 'Be thorough but concise. Include key points.'
: 'Keep it brief and to the point.'

return `Act as ${role}.

Task: ${requirement}

Tone: ${tone}

Instructions:
${detailInstructions}

Please provide:
1. A clear, structured response
2. Relevant examples where applicable
3. Actionable takeaways

Format your response using markdown with appropriate headings and bullet points.`
}

export function PromptGenerator() {
const [requirement, setRequirement] = useState('')
const [category, setCategory] = useState('Writing')
const [tone, setTone] = useState('professional')
const [detail, setDetail] = useState(5)
const [result, setResult] = useState('')
const [loading, setLoading] = useState(false)
const [copied, setCopied] = useState(false)

const handleGenerate = async () => {
if (!requirement.trim()) return
setLoading(true)
// Simulate API call
await new Promise(r => setTimeout(r, 1500))
setResult(generateMockPrompt(requirement, category, tone, detail))
setLoading(false)
}

const handleCopy = async () => {
await navigator.clipboard.writeText(result)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}

return (
<div className="mx-auto max-w-4xl px-4 py-12">
<div className="rounded-2xl border border-gray-200 bg-white p-8 shadow-sm">
<h2 className="text-2xl font-bold text-gray-900">Generate Your Prompt</h2>
<p className="mt-2 text-gray-600">
Describe what you want to achieve, and we'll create the perfect prompt.
</p>

<div className="mt-8 space-y-6">
{/* Requirement Input */}
<div>
<label className="block text-sm font-medium text-gray-700">
What do you want to do?
</label>
<textarea
rows={3}
value={requirement}
onChange={(e) => setRequirement(e.target.value)}
placeholder="e.g., Write a blog post about sustainable living..."
className="mt-2 w-full rounded-xl border border-gray-300 px-4 py-3 text-sm focus:border-violet-500 focus:outline-none focus:ring-2 focus:ring-violet-500/20"
/>
</div>

{/* Category & Tone */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700">Category</label>
<select
value={category}
onChange={(e) => setCategory(e.target.value)}
className="mt-2 w-full rounded-xl border border-gray-300 px-4 py-3 text-sm"
>
{categories.map(c => (
<option key={c} value={c}>{c}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Tone</label>
<select
value={tone}
onChange={(e) => setTone(e.target.value)}
className="mt-2 w-full rounded-xl border border-gray-300 px-4 py-3 text-sm"
>
{tones.map(t => (
<option key={t.value} value={t.value}>{t.label}</option>
))}
</select>
</div>
</div>

{/* Detail Level */}
<div>
<label className="block text-sm font-medium text-gray-700">
Detail Level: {detail}/10
</label>
<input
type="range"
min={1}
max={10}
value={detail}
onChange={(e) => setDetail(Number(e.target.value))}
className="mt-2 w-full accent-violet-600"
/>
</div>

{/* Generate Button */}
<button
onClick={handleGenerate}
disabled={loading || !requirement.trim()}
className="w-full inline-flex items-center justify-center rounded-xl bg-violet-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-violet-700 disabled:opacity-50 transition-colors"
>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Generating...
</>
) : (
<>
<Sparkles className="mr-2 h-4 w-4" />
Generate Prompt
</>
)}
</button>
</div>
</div>

{/* Result */}
{result && (
<div className="mt-8 rounded-2xl border border-gray-200 bg-white p-8 shadow-sm">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-gray-900">Your Prompt</h3>
<button
onClick={handleCopy}
className="inline-flex items-center gap-2 rounded-lg border border-gray-200 px-3 py-2 text-sm text-gray-600 hover:bg-gray-50"
>
{copied ? (
<><Check className="h-4 w-4 text-green-500" /> Copied!</>
) : (
<><Copy className="h-4 w-4" /> Copy</>
)}
</button>
</div>
<pre className="mt-4 whitespace-pre-wrap rounded-xl bg-gray-50 p-4 text-sm text-gray-800">
{result}
</pre>
</div>
)}
</div>
)
}

第四轮:SEO Meta 标签 & 布局

我的 Prompt:

Add proper SEO meta tags to the layout.tsx:

  • Default title and description
  • Open Graph tags
  • Twitter card tags
  • Add a Header component with navigation
  • Add a Footer component with links

Codex 生成的布局代码: