从零构建ChatGPT对话前端页面:新手入门指南与实战代码

1次阅读
没有评论

共计 4227 个字符,预计需要花费 11 分钟才能阅读完成。

image.webp

ChatGPT 的 API 为开发者提供了强大的自然语言处理能力,通过简单的 HTTP 请求即可实现智能对话功能。对于前端开发者来说,将其集成到网页应用中可以大大提升用户体验。本文将带你从零开始构建一个完整的 ChatGPT 对话前端页面。

从零构建 ChatGPT 对话前端页面:新手入门指南与实战代码

技术栈选择

在开始之前,我们先对比一下主流前端框架在这个场景下的表现:

  • React:组件化开发模式非常适合构建交互式对话界面,丰富的生态系统和 hooks 特性简化了状态管理
  • Vue:虽然也可以胜任,但在处理复杂状态流时略显不足
  • 纯 JavaScript:开发效率低,维护成本高

综合考虑开发效率和项目可维护性,我们推荐使用 React + TypeScript 技术栈。TypeScript 的类型系统能帮助我们更好地管理 API 响应数据结构和组件状态。

核心实现

1. API 请求封装与鉴权处理

首先我们需要封装与 ChatGPT API 的通信逻辑。这里使用 Fetch API 来实现:

interface Message {
  role: 'user' | 'assistant';
  content: string;
}

async function fetchChatResponse(messages: Message[], apiKey: string) {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`
    },
    body: JSON.stringify({
      model: 'gpt-3.5-turbo',
      messages: messages,
      stream: true // 启用流式响应
    })
  });

  if (!response.ok) {throw new Error(`API 请求失败: ${response.status}`);
  }

  return response;
}

2. 对话流式接收与渲染

流式接收可以显著改善用户体验,让回复能够逐步显示而不是等待完整响应:

async function processStreamResponse(
  response: Response, 
  onDataReceived: (chunk: string) => void
) {const reader = response.body?.getReader();
  const decoder = new TextDecoder();
  let result = '';

  if (!reader) return;

  while (true) {const { done, value} = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value, { stream: true});
    const lines = chunk.split('\n');

    for (const line of lines) {if (line.startsWith('data:') && !line.includes('[DONE]')) {const data = JSON.parse(line.replace('data:', ''));
        const content = data.choices[0]?.delta?.content || '';
        result += content;
        onDataReceived(result);
      }
    }
  }
}

3. 用户输入防抖与错误处理

为了防止频繁触发 API 请求,我们需要对用户输入进行防抖处理:

function debounce<F extends (...args: any[]) => void>(
  func: F, 
  delay: number
): (...args: Parameters<F>) => void {
  let timeoutId: ReturnType<typeof setTimeout>;

  return function(...args: Parameters<F>) {clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func(...args), delay);
  };
}

完整 React 组件实现

下面是一个完整的 ChatGPT 对话组件实现:

import React, {useState, useRef} from 'react';

type Message = {
  role: 'user' | 'assistant';
  content: string;
};

const ChatInterface: React.FC<{apiKey: string}> = ({apiKey}) => {const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const messagesEndRef = useRef<HTMLDivElement>(null);

  const handleSubmit = async (e: React.FormEvent) => {e.preventDefault();
    if (!input.trim() || isLoading) return;

    const userMessage: Message = {role: 'user', content: input};
    setMessages(prev => [...prev, userMessage]);
    setInput('');
    setIsLoading(true);
    setError(null);

    try {const response = await fetchChatResponse([...messages, userMessage], apiKey);
      let assistantMessage = '';

      setMessages(prev => [...prev, { role: 'assistant', content: ''}]);

      await processStreamResponse(response, (chunk) => {
        assistantMessage = chunk;
        setMessages(prev => {const newMessages = [...prev];
          newMessages[newMessages.length - 1].content = assistantMessage;
          return newMessages;
        });
        scrollToBottom();});
    } catch (err) {setError(err instanceof Error ? err.message : '未知错误');
    } finally {setIsLoading(false);
    }
  };

  const scrollToBottom = () => {messagesEndRef.current?.scrollIntoView({ behavior: 'smooth'});
  };

  return (
    <div className="chat-container">
      <div className="messages">
        {messages.map((msg, i) => (<div key={i} className={`message ${msg.role}`}>
            {msg.content}
          </div>
        ))}
        {isLoading && (
          <div className="message assistant">
            <div className="typing-indicator"> 思考中...</div>
          </div>
        )}
        <div ref={messagesEndRef} />
      </div>

      {error && <div className="error">{error}</div>}

      <form onSubmit={handleSubmit} className="input-area">
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="输入消息..."
          disabled={isLoading}
        />
        <button type="submit" disabled={isLoading || !input.trim()}>
          发送
        </button>
      </form>
    </div>
  );
};

生产环境注意事项

API 调用频率限制处理

ChatGPT API 有调用频率限制,我们需要在客户端实现适当的节流:

// 在组件中使用
const throttledSubmit = useMemo(() => throttle(handleSubmit, 1000), []);

敏感内容过滤方案

可以在发送到 API 前对用户输入进行初步过滤:

function filterSensitiveContent(text: string): string {const sensitivePatterns = [/badword1/gi, /badword2/gi];
  return sensitivePatterns.reduce((result, pattern) => 
    result.replace(pattern, '***'), text);
}

移动端适配要点

确保在移动设备上有良好的体验:

.chat-container {
  max-width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.input-area {
  padding: 10px;
  position: sticky;
  bottom: 0;
  background: white;
}

进阶思考

  1. 对话历史本地存储 :如何使用 localStorage 或 IndexedDB 保存对话历史,实现页面刷新后恢复对话?
  2. 多轮对话上下文保持 :如何设计数据结构来维护对话上下文,确保 ChatGPT 能理解连续对话?
  3. 性能优化指标监控 :如何监控 API 响应时间、首字节时间等关键指标,并据此优化用户体验?

通过本文的学习,你已经掌握了构建 ChatGPT 对话前端页面的核心技能。建议从简单实现开始,逐步添加上述进阶功能,不断优化用户体验。

正文完
 0
评论(没有评论)