Next.js+AI实战:从零三天上线Product Hunt的极简智能摘要工具

作者:技术拾光时间:2026-06-09 06:28:07浏览:491

一个极简智能摘要工具从零开发到上线ProductHunt仅用三天,获得当日Top5。产品仅含三个功能:粘贴长文、AI生成三句话摘要、一键复制。采用Next.js14作为前后端框架,后端通过OpenAI流式接口透传SSE实现打字机效果,前端逐字渲染,样式克制,部署于Vercel,聚焦核心功能并控制成本。

上个月我开发了一款基于AI的三句话摘要工具,用户只需粘贴一篇长文,AI即可智能生成精准摘要。没有复杂的后台管理系统,没有用户体系,没有付费墙,仅需一个输入框、一个按钮,即可获得一段漂亮的输出。这款工具仅用三天完成开发,第四天上线Product Hunt,当天便获得了Top 5的成绩。本文完整记录技术实现过程,代码虽不多,但每一行都经过反复删减,保留最核心部分。

一、产品设计:少即是多

1.1 功能清单

整个AI摘要工具仅包含三个功能:

  1. 粘贴或输入长文本
  2. 点击按钮,AI生成三句话摘要
  3. 一键复制摘要

极致简洁,不附带任何冗余功能。

graph LRA["用户输入长文"] --> B["调用 AI API"]B --> C["流式输出摘要"]C --> D["一键复制"]style B fill:#8b5cf6,color:#fff

1.2 技术选型

层 选择 理由
框架 Next.js 14 (App Router) 前后端一体,Route Handler 直接当 API
样式 CSS Modules 不引入额外依赖,够用就好
AI OpenAI gpt-4o-mini 便宜、快、摘要质量够
部署 Vercel 零配置,推完代码自动上线

独立开发的一个原则:能少引一个依赖就少引一个。每多一个 npm install,就多一份未来的维护债。

二、核心实现

2.1 项目结构

summarizer/├── app/│   ├── layout.tsx        # 全局布局│   ├── page.tsx         # 首页(唯一页面)│   ├── page.module.css  # 首页样式│   └── api/│       └── summarize/│           └── route.ts # AI 摘要接口├── components/│   ├── TextInput.tsx    # 输入框组件│   ├── SummaryOutput.tsx # 摘要输出组件│   └── CopyButton.tsx   # 复制按钮├── lib/│   └── openai.ts       # OpenAI 封装└── package.json

一共8个文件,每个文件承担明确职责,不多不少。

2.2 后端:流式 AI 接口

// app/api/summarize/route.tsimport { NextRequest } from 'next/server';export async function POST(req: NextRequest) {  const { text } = await req.json();  // 输入校验:不废话,直接拦  if (!text || text.length < 50) {    return new Response(JSON.stringify({ error: '文本太短,至少 50 个字' }), { status: 400 });  }  if (text.length > 10000) {    return new Response(JSON.stringify({ error: '文本太长,最多 10000 个字' }), { status: 400 });  }  // 调用 OpenAI 流式接口  const response = await fetch('https://api.openai.com/v1/chat/completions', {    method: 'POST',    headers: {      'Content-Type': 'application/json',      'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,    },    body: JSON.stringify({      model: 'gpt-4o-mini',      stream: true,      temperature: 0.3, // 摘要场景用低温度,输出更稳定      messages: [        {          role: 'system',          content: '你是一个专业的文本摘要助手。请用三句话概括用户提供的文章核心内容。要求:第一句点明主题,第二句总结关键论点,第三句给出结论或启发。语言精炼,不要套话。',        },        {          role: 'user',          content: `请摘要以下文章:${text}`,        },      ],    }),  });  // 直接透传 SSE 流给前端  return new Response(response.body, {    headers: {      'Content-Type': 'text/event-stream',      'Cache-Control': 'no-cache',      'Connection': 'keep-alive',    },  });}

这里直接将OpenAI的SSE流透传给前端,不做中间缓存和二次封装,数据流向越短,延迟越低。

2.3 前端:流式渲染

// components/SummaryOutput.tsx'use client';import { useState } from 'react';import styles from './SummaryOutput.module.css';interface Props {  text: string;  onComplete: (summary: string) => void;}export default function SummaryOutput({ text, onComplete }: Props) {  const [summary, setSummary] = useState('');  const [loading, setLoading] = useState(false);  const handleSummarize = async () => {    setLoading(true);    setSummary('');    const response = await fetch('/api/summarize', {      method: 'POST',      headers: { 'Content-Type': 'application/json' },      body: JSON.stringify({ text }),    });    if (!response.ok || !response.body) {      setSummary('摘要生成失败,请重试');      setLoading(false);      return;    }    // 流式读取 SSE    const reader = response.body.getReader();    const decoder = new TextDecoder();    let buffer = '';    let fullText = '';    while (true) {      const { done, value } = await reader.read();      if (done) break;      buffer += decoder.decode(value, { stream: true });      const lines = buffer.split('\n');      buffer = lines.pop() || ''; // 最后一行可能不完整,留到下次      for (const line of lines) {        if (line === 'data: [DONE]') continue;        if (!line.startsWith('data: ')) continue;        try {          const json = JSON.parse(line.slice(6));          const content = json.choices.[0].delta.content || '';          fullText += content;          setSummary(fullText); // 逐字更新,打字机效果        } catch {          // 跳过解析失败的行        }      }    }    setLoading(false);    onComplete(fullText);  };  return (    
{summary && (

{summary}

{!loading && }
)}
);}

流式读取SSE并逐字更新,打造打字机效果,让用户实时看到AI的“思考输出”。

2.4 一键复制

// components/CopyButton.tsx'use client';import { useState } from 'react';import styles from './CopyButton.module.css';export default function CopyButton({ text }: { text: string }) {  const [copied, setCopied] = useState(false);  const handleCopy = async () => {    try {      await na vigator.clipboard.writeText(text);      setCopied(true);      setTimeout(() => setCopied(false), 2000);    } catch {      // 降级方案:用 textarea 选中复制      const textarea = document.createElement('textarea');      textarea.value = text;      document.body.appendChild(textarea);      textarea.select();      document.execCommand('copy');      document.body.removeChild(textarea);      setCopied(true);      setTimeout(() => setCopied(false), 2000);    }  };  if (!text) return null;  return (      );}

复制功能简洁直接:优先使用Clipboard API,降级方案则用textarea选中复制,覆盖所有浏览器场景。

三、样式:克制的美

3.1 设计原则

整个UI仅使用黑、白、一个强调色,不用渐变,不用阴影堆砌。

/* app/page.module.css */.main {  max-width: 640px;  margin: 0 auto;  padding: 4rem 1.5rem;  font-family: -apple-system, 'Noto Sans SC', sans-serif;}.title {  font-size: 1.5rem;  font-weight: 600;  color: #1a1a1a;  margin-bottom: 0.5rem;}.subtitle {  font-size: 0.875rem;  color: #888;  margin-bottom: 2rem;}.textarea {  width: 100%;  min-height: 200px;  padding: 1rem;  border: 1px solid #e5e5e5;  border-radius: 8px;  font-size: 0.9375rem;  line-height: 1.6;  resize: vertical;  transition: border-color 0.2s;  font-family: inherit;}.textarea:focus {  outline: none;  border-color: #8b5cf6;}/* 输出区域:打字机闪烁光标 */.output {  margin-top: 1.5rem;  padding: 1.25rem;  background: #fafafa;  border-radius: 8px;  line-height: 1.8;  color: #333;}.cursor {  display: inline-block;  width: 2px;  height: 1em;  background: #8b5cf6;  animation: blink 0.8s infinite;  vertical-align: text-bottom;  margin-left: 2px;}@keyframes blink {  0%, 50% { opacity: 1; }  51%, 100% { opacity: 0; }}

一个紫色作为强调色,一个闪烁光标模拟打字机效果,这正是“克制的美”。

四、部署:三条命令上线

# 第一步:环境变量echo "OPENAI_API_KEY=sk-你的密钥" > .env.local# 第二步:推到 GitHubgit add . && git commit -m "feat: 极简摘要工具" && git push# 第三步:在 Vercel 导入项目# 打开 vercel.com -> Import -> 选择仓库 -> 填入环境变量 -> Deploy# 完事。

Vercel会自动检测Next.js项目,零配置部署。自带HTTPS、CDN、Edge Runtime,开箱即用。

五、避坑指南

5.1 流式透传的陷阱

Vercel的Edge Runtime和Node.js Runtime对流式的支持不同:

// 如果用 Edge Runtime(推荐,更快)export const runtime = 'edge';// 如果用 Node.js Runtime,需要用 ReadableStream 手动包一层// Edge Runtime 可以直接透传 response.body

5.2 成本控制

GPT-4o-mini很便宜,但不限制的话仍可能被刷。

// 简单的速率限制(基于 IP)const rateLimitMap = new Map();function checkRateLimit(ip: string): boolean {  const now = Date.now();  const windowMs = 60 * 1000; // 1 分钟窗口  const maxRequests = 10; // 每分钟最多 10 次  const timestamps = rateLimitMap.get(ip) || [];  const recent = timestamps.filter(t => now - t < windowMs);  if (recent.length >= maxRequests) {    return false; // 超限  }  recent.push(now);  rateLimitMap.set(ip, recent);  return true;}

5.3 SEO 基础

code>// app/layout.tsxexport const metadata = {  title: '三句话摘要 - AI 智能文本摘要工具',  description: '粘贴任意长文,AI 帮你用三句话精准概括核心内容。免费、快速、无需注册。',};

六、总结

三天,8个文件,一个能用的AI摘要产品。技术栈越简单,迭代速度越快。不要在Day1就引入Redis、数据库、用户系统。先把核心价值跑通,让用户用起来,再按需添加。好的代码,是删出来的。