96SEO 2026-04-24 19:03 0
用户对于等待的耐心几乎降到了零点。试想一下当你向 ChatGPT 提问时Ru果它盯着你kan上十秒钟,然后突然“啪”地一下甩出几千字的完整回答,那种体验绝对是灾难性的。我们早Yi习惯了那种文字像打字机一样逐字蹦出来的感觉——这不仅仅是一种视觉特效,geng是对“系统正在思考、正在响应”的心理确认。

Zui近在折腾我的 Next.js 项目时我决定把这种丝滑的流式体验接入进来。但这事儿远比我想象的要复杂。AI 返回的往往不是规规矩矩的纯文本,而是半完整的 JSON 片段,甚至夹杂着各种格式噪音。Ru果直接照搬传统的 API 请求模式,前端大概率会直接崩溃。今天我就把这几天踩过的坑、出的方案,毫无保留地分享出来。
一、 为什么传统的“等一等”模式行不通?这就是噩梦。
大模型生成内容是一个逐 Token解码的过程。Ru果等它全部生成完再传给前端,用户就要盯着白屏发呆。为了解决这个问题,业界主流的方案是流式传输。简单来说就是水管里还没装满水,水龙头就开始滴水了。
技术选型:WebSocket 还是 HTTP Streaming?提到实时通信,hen多人第一反应是 WebSocket。确实WebSocket 是全双工通信,hen强,但它有点“杀鸡用牛刀”。
WebSocket: 建立连接开销大,协议复杂,维护成本高。它适合双向频繁交互,比如游戏、即时通讯。对于单纯的“AI 生成文本”这种单向流,用 WebSocket 就像开着法拉利去买菜,成本高且没必要。
SSE : 虽然是标准的服务器推送方案,但在某些跨端环境下支持情况参差不齐,兼容性是个大坑。
HTTP Streaming : 这是我Zui终的选择。它基于标准的 HTTP 协议,利用 fetch API 的流式读取Neng力,轻量、通用,且完美契合 Next.js 的 Route Handler 架构。
我们的目标hen明确:用户输入 → Next.js 后端转发给 AI→ AI 边生成边推回 Next.js → Next.js 原封不动地透传给前端 → 前端边收边渲染。
这里有一个关键点:Next.js 不会帮我兜底。它只负责传输,不负责理解内容。理解内容、处理乱码、拼装 JSON,这些脏活累活全得前端自己扛。
1. 后端实现:构建流式转发通道在 Next.js 的 App Router 中,我们Ke以利用 ReadableStream 来构建一个高效的透传通道。下面的代码展示了如何将 DeepSeek 的流直接“管”到前端。
// app/api/ai/route.ts
// 系统提示词
const systemPrompt = `你是一个专业的问卷生成器,必须严格按照 JSON 格式输出...`;
export async function POST {
// 1. 获取用户输入的主题
const { topic } = await req.json;
try {
// 2. 向 DeepSeek 发起请求,务必开启 stream: true
const response = await fetch("https://api.deepseek.com/chat/completions", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.DEEPSEEK_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "deepseek-chat",
stream: true, // ⚠️ 这里是灵魂开关
messages: ,
}),
});
// 3. 创建一个可读流,用于将上游的数据接力给下游
const stream = new ReadableStream({
async start {
// 拿到 DeepSeek 返回的读取器
const reader = response.body!.getReader;
while {
const { done, value } = await reader.read;
if break;
// 核心逻辑:不加工、不修改,直接透传数据块
controller.enqueue;
}
controller.close;
},
});
// 4. 返回流,并设置 SSE 兼容的头信息
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
},
});
} catch {
return new Response;
}
}
三、 前端攻坚:处理不稳定的 AI 数据流
后端只是个搬运工,真正的挑战在前端。AI 返回的数据流长这样:
data: {"choices":}
data: {"choices":}
data:
注意到了吗?它不是纯文本,而是带有 data: 前缀的 JSON 片段。Ru果你直接 JSON.parse,程序必挂无疑。我们需要一套精细的“清洗-拼接-展示”流程。
网络传输是切分成一个个 Chunk的,一个 JSON 对象可Neng会被切断在两个 Chunk 之间。比如前一个 Chunk 是 {"type": "in,下一个 Chunk 才是 put"}。直接解析必报错。
所以我们需要一个 buffer来暂存不完整的数据。
// 全局存储当前请求的控制器,用于取消生成
let currentController = null;
async function generate {
const controller = new AbortController;
currentController = controller;
try {
const res = await fetch('/api/ai', {
method: 'POST',
body: JSON.stringify,
signal: controller.signal,
});
const reader = res.body.getReader;
const decoder = new TextDecoder;
let buffer = ''; // 临时存储不完整行
let fullText = ''; // Zui终拼接完整的文本
while {
const { done, value } = await reader.read;
if break;
// 解码当前数据块
const chunk = decoder.decode;
buffer += chunk;
// 按换行符分割
const lines = buffer.split;
// Zui后一行可Neng不完整,留到下一轮拼接
buffer = lines.pop || '';
for {
// 去掉 "data: " 前缀
const clean = line.replace.trim;
// 跳过空行或结束标记
if continue;
try {
const parsed = JSON.parse;
// 提取有效内容
const content = parsed.choices?.?.delta?.content;
if {
fullText += content;
// 实时geng新 UI,制造打字机效果
setGeneratingText;
}
} catch {
// 即使某行解析失败也不中断流,容错处理
console.warn;
}
}
}
// 流结束后的Zui终处理
handleFinalResult;
} catch {
console.log;
}
}
2. JSON 解析与容错:给 AI 的“脏数据”擦屁股
AI 是不可控的。它可Neng返回一段被 Markdown 代码块包裹的 JSON,或者在 JSON 前面加了一句“好的,这是您要的数据”。直接 JSON.parse 肯定会炸。我们需要一个“暴力”解析函数。
// 安全解析 JSON
function safeParseJSON {
try {
// 理想情况:直接就是标准 JSON
return JSON.parse;
} catch {
// 失败情况:尝试在文本中提取Zui外层的 JSON 结构
// 使用正则匹配第一个 { 到Zui后一个 }
const match = text.match;
if {
try {
return JSON.parse;
} catch {
// 还是失败?那就没办法了
return null;
}
}
return null;
}
}
3. Schema 校验:防止页面崩溃的Zui后一道防线
即使解析出了 JSON,字段缺失怎么办?比如组件缺了 type,或者缺了默认值。直接渲染会导致 UI 抖动甚至白屏。我们需要Zuo数据标准化。
// 校验数据格式
function validateSchema {
if return false;
if return false;
return true;
}
// 数据标准化:缺什么补什么
function normalizeSchema {
const result = {};
// 遍历组件,补全默认值
Object.entries.forEach => {
result = {
type: item.type || 'input', // 缺类型默认为输入框
props: item.props || {}, // 缺配置默认空对象
style: item.style || {}, // 缺样式默认空对象
children: item.children || , // 缺子元素默认空数组
};
});
return {
components: result,
order: schema.order || Object.keys, // 保证渲染顺序
};
}
// Zui终处理函数
function handleFinalResult {
const parsed = safeParseJSON;
if ) {
console.error;
return;
}
// 数据兜底处理
const standardSchema = normalizeSchema;
// 存入预览状态,等待用户确认
setPreviewSchema;
}
四、 交互优化:让用户掌控节奏
流式输出不仅是kan个热闹,还得Neng控制。Zui常见的需求就是“停止生成”。利用 AbortController Ke以轻松实现。
function cancelGenerate {
if {
currentController.abort; // 立即中断 fetch 请求
console.log;
}
}
此外在流结束并解析出正确的 JSON 后不要直接扔到画布上。Zui好先给用户一个“预览”状态,让用户确认 AI 生成的东西没跑偏,再点击“确认加入画布”。这种“生成 → 预览 → 确认”的闭环,Neng有效避免 AI 幻觉带来的污染。
五、 与思考经过这一通折腾,我深刻体会到:流式渲染完美解决了 AI 场景的“等待焦虑”,但也引入了前端状态管理的复杂性。
在这次方案设计中,我Zui终放弃了“组件级流式渲染”,因为那会导致页面频繁重绘、布局抖动,甚至因为 JSON 不完整而报错。我选择了geng务实的Zuo法:流式仅用于文本展示,待完整 JSON 接收完毕并经过清洗、校验后再一次性渲染组件。
这种“生成与渲染解耦”的思路,虽然牺牲了一点点极致的实时感,但换来了系统的健壮性和用户体验的稳定性。毕竟一个不崩溃的产品,比一个炫酷但经常报错的产品要强得多。
后续,我还在考虑加入请求缓存、超时重试以及增量渲染优化。Ru果你也在Zuo类似的 AI 应用,希望这些踩坑经验Neng帮你省下几根头发。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback