12.3.4 用户体验优化——加载状态与错误处理:优雅的 AI 交互设计
一句话破题
AI 应用的用户体验不仅取决于模型能力,更取决于如何处理加载、错误和边缘情况——这是区分"能用"和"好用"的关键。
核心价值
优秀的 AI 应用需要处理以下场景:
- 加载状态:让用户知道 AI 正在思考
- 流式显示:逐字显示,而非突然出现
- 错误恢复:出错时提供重试选项
- 取消操作:允许用户中途停止
- 空状态:初始状态的引导
加载状态设计
tsx
function LoadingIndicator() {
return (
<div className="flex items-center gap-2 text-gray-500">
<div className="flex gap-1">
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
</div>
<span>AI 正在思考...</span>
</div>
);
}
function ChatUI() {
const { messages, isLoading, ... } = useChat();
return (
<div>
{messages.map((m) => (
<MessageBubble key={m.id} message={m} />
))}
{isLoading && <LoadingIndicator />}
</div>
);
}打字机效果优化
默认的流式输出已经是逐字显示,但可以进一步优化:
tsx
function StreamingMessage({ content }: { content: string }) {
return (
<div className="relative">
<span>{content}</span>
<span className="inline-block w-2 h-4 bg-gray-800 animate-pulse ml-1" />
</div>
);
}错误处理与重试
tsx
function ChatWithErrorHandling() {
const { messages, error, reload, isLoading } = useChat({
onError: (error) => {
// 可以接入错误监控服务
console.error('Chat error:', error);
},
});
if (error) {
return (
<div className="p-4 bg-red-50 border border-red-200 rounded-lg">
<div className="flex items-center gap-2 text-red-700">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
<span>出错了:{error.message}</span>
</div>
<button
onClick={() => reload()}
className="mt-2 px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700"
>
重试
</button>
</div>
);
}
// ... 正常渲染
}取消生成
tsx
function ChatWithStop() {
const { isLoading, stop, handleSubmit, ... } = useChat();
return (
<form onSubmit={handleSubmit}>
{/* ... 输入框 */}
{isLoading ? (
<button
type="button"
onClick={stop}
className="px-4 py-2 bg-red-500 text-white rounded"
>
停止生成
</button>
) : (
<button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded">
发送
</button>
)}
</form>
);
}空状态引导
tsx
function EmptyState() {
const suggestions = [
'帮我写一段自我介绍',
'解释什么是 React Hooks',
'给我讲个笑话',
];
return (
<div className="flex flex-col items-center justify-center h-full text-center p-8">
<h2 className="text-2xl font-bold mb-4">有什么可以帮你的?</h2>
<p className="text-gray-500 mb-6">试试以下问题:</p>
<div className="flex flex-wrap gap-2 justify-center">
{suggestions.map((s) => (
<button
key={s}
onClick={() => append({ role: 'user', content: s })}
className="px-4 py-2 bg-gray-100 rounded-full hover:bg-gray-200"
>
{s}
</button>
))}
</div>
</div>
);
}
function Chat() {
const { messages, append, ... } = useChat();
return (
<div>
{messages.length === 0 ? (
<EmptyState />
) : (
// ... 消息列表
)}
</div>
);
}AI 协作指南
- 核心意图:让 AI 帮你设计完善的交互状态。
- 需求定义公式:
"请帮我实现一个 AI 聊天界面的完整状态处理,包括加载动画、错误提示、重试按钮和停止生成功能。" - 关键术语:
isLoading、error、reload、stop、空状态 (empty state)
避坑指南
- 加载状态要有视觉反馈:不要让用户猜测是否在工作。
- 错误信息要可理解:将技术错误转换为用户友好的提示。
- 提供逃生出口:用户应该能够取消长时间运行的操作。
