Next.js AI智能摘要工具:从零到Product Hunt上线全指南

2026-06-09阅读 0热度 0
智能摘要

上个月我构建了一个轻量级工具:用户粘贴任意长文,AI 自动提炼三句核心摘要。

没有后台管理、没有用户系统、没有付费门槛。只有一个输入框、一个按钮、一段即时生成的摘要。

开发耗时三天,第四天提交到 Product Hunt 并拿下当日 Top 5。

本文完整拆解技术实现细节。代码量不多,但每行都经过反复删减,只保留最必要的逻辑。

一、产品设计:减法原则

1.1 功能清单

整个工具仅包含三项操作:

  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 基础

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

六、总结

三天时间,8 个文件,一个能直接使用的产品。

技术栈越精简,迭代速度越快。不要在第一天就引入 Redis、数据库、用户系统。先把核心价值跑通,让用户用上,再根据实际需求逐步扩展。

好代码,是删出来的。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策