探索ChatGPT背后的前端黑科技

news/2025/2/13 10:17:48/文章来源:https://www.cnblogs.com/98kk/p/18712567

由于图片和格式解析问题,可前往 阅读原文

在人工智能与互联网技术飞速发展的今天,像ChatGPT这样的智能对话系统已经成为科技领域的焦点。它不仅能够进行自然流畅的对话,还能以多种格式展示内容,为用户带来高效且丰富的交互体验。然而,这些令人惊叹的功能背后,离不开前端技术的支持与实现

本文将深入探索ChatGPT背后的前端黑科技,希望能为开发者提供有价值的参考,帮助他们在实际项目中更高效地实现类似的功能

以下内容仅是本人的一次思考,如有更好方案的可在评论区留言

:::warning 小贴士
文章中涉及到的示例代码你都可以从 这里查看 ,若对你有用还望点赞支持
:::

单页面应用(SPA)

单页面应用是指在整个使用过程中,只有一个HTML页面被加载。所有的导航和交互操作都是在前端通过JavaScript完成,无需重新加载整个页面。这使得用户可以在同一个页面内无缝浏览不同的内容

虽然在开发过程中可能会遇到一些挑战(比如SEO优化),但SPA凭借其优势已成为现代Web开发的主流趋势。相信大家都对React/Vue/Angular/Svelte等都不陌生,就不多做介绍了

实时通讯

实时通讯的总要性以及使用场景就不多说了,来看下常用的几种通讯技术

Server-Sent Events(SSE)

如果你仔细查看了ChatGPT的对话请求过程就会看到,服务器在不断的推送数据

这里用到的就是SSE技术,基于HTTP协议单向推送技术。它使得服务器能够在数据生成时实时推送给客户端,而无需客户端频繁轮询服务器。这非常适合需要实时更新的应用场景,如新闻推送、股票价格监控和日志跟踪

使用SSE注意事项

  • 在响应头中添加Content-Type: text/event-stream,以通知客户端这是一个SSE连接
  • 返回的数据要包含data: xxx,并以data: xxx\n\n双换行符格式,这样客户端才能正确解析,可以指定id、type等数据;默认情况下事件都是message,也可以自定义事件
  • 建立好后的sse链接就会不断发数据,直到EventSource调用close事件
  • 客户端使用EventSource来接收数据

编写客户端页面代码:

const eventSource = new EventSource('http://localhost:3000/sse');eventSource.addEventListener("message", (e) => {console.log(e);if (e.data >> 0 > 4) eventSource.close();  // 客户端根据数据判断然后主动断开连接
});// 服务器如果判断数据发完了可以发送type为done的数据包,客户端就可以监听到此类事件
eventSource.addEventListener("done", (e) => {});eventSource.addEventListener('error', (e) => {console.log('Connection failed:', e);
});

使用express搭建服务器:

app.get("/sse", (req, res) => {// 设置响应头为SSE格式(必填的)res.setHeader("Content-Type", "text/event-stream");res.setHeader("Cache-Control", "no-cache");res.setHeader("Connection", "keep-alive");let count = 0; let timer;const sendEvent = () => {res.write(`id: ${count}\n\n`);res.write(`data: ${count}\n\n`);count++;timer = setTimeout(sendEvent, 1000);};sendEvent();req.on("close", () => {console.log("Client disconnected");clearTimeout(timer);});
});

推荐使用Nest框架搭建sse服务器,更简单强大:

@Controller("/api/sse")
export class SseController {private subject = new Subject<void>();@Sse("msgs")sse(@Query("msg") msg: string): Observable<MessageEvent> {const eventStream = Array.from({ length: 5 }, (v, i) => i).map((message, index) =>of({data: `from sse events: ${index}, 你发送了 【${msg}】`,id: randomUUID(),retry: 0,type: index === 4 ? "done" : "message",} as MessageEvent).pipe(delay(1000)),takeUntil(this.subject),);return concat(...eventStream);}// 主动停止@Post("/stop")stop() {return this.subject.next();}
}

Websocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许客户端和服务端之间建立持久连接,实现双向实时数据传输

相比sse支持双向通讯、实现更加复杂,适合在线客服、实时消息通知、在线文档编辑、白板共享等等,不过也有在ChatGPT类似应用中用到的

客户端页面代码示例:

// 连接到 WebSocket 服务器
const ws = new WebSocket('ws://localhost:8080');ws.onopen = () => { console.log('Connected to server'); };
ws.onmessage = (event) => { console.log(`Received message: ${event.data}`); };
ws.onclose = () => { console.log('Connection closed'); };

node创建Websocket服务器:

const WebSocket = require('ws');// 创建一个 HTTP 服务器(可选)
const http = require('http');
const server = http.createServer((req, res) => {res.writeHead(404, {'Content-Type': 'text/plain'});res.end('Not Found');
});// 使用 ws 库创建 WebSocket 服务器
const wss = new WebSocket.Server({ server });wss.on('connection', (ws) => {console.log('New client connected');// 发送消息到客户端ws.send('Hello from server!');// 接收客户端消息ws.on('message', (message) => {console.log(`Received message: ${message}`);// 回复客户端ws.send(`Echo back: ${message}`);});// 处理客户端断开连接ws.on('close', () => {console.log('Client disconnected');});
});

Chunked Transfer

HTTP 分块传输(HTTP Chunked Transfer)是一种将大数据量分解为多个较小的数据块进行传输的技术。每个数据块被称为“chunk”,这些chunk独立地通过网络传输,并在接收端重新组装成原始数据

这种方法特别适用于需要逐步处理数据的场景,例如视频流媒体和大文件下载,使用看起来和sse很像,也是可以用在这种gpt这种场景的

使用express编写示例:

app.use("/chunked", (req, res) => {// 必须要设置Transfer-Encoding头信息res.setHeader("Transfer-Encoding", "chunked");let timer, i = 1;// 1s返回一次 总共返回9次timer = setInterval(() => {res.write(`${i}`);if (i >= 10) {clearInterval(timer);res.end();}i++;}, 1000);
});

现在请求这个接口看下效果:

模拟打印

是不是看到ChatGPT的不断打印文字的效果很有感觉,做到这一点也并不难,下面是一个简单实现的🌰

// 模拟 ChatGPT 打印文字效果
function simulateChatGPTTyping(outputElement, text) {let index = 0;outputElement.innerHTML = '';function typeText() {if (index < text.length * 2) {// 随机延迟,模拟思考时间setTimeout(() => {// 随机选择一个字符来打字const randomIndex = Math.floor(Math.random() * text.length);const char = text[randomIndex];// 更新输出内容outputElement.innerHTML += char;// 滚动到最新位置outputElement.scrollTop = outputElement.scrollHeight;index++;typeText();}, 10 + Math.random() * 200); // 延迟范围:100-300ms} else {// 打印完成,添加换行符setTimeout(() => {outputElement.innerHTML += '<br>';}, 500);}}typeText();
}// 示例文本
const text = `我是人工智能助手ChatGPT。我可以帮助你回答问题、提供信息和进行对话。你可以问我任何你感兴趣的问题,我会尽力为你提供详细的解答。例如,你可以问我关于科技、历史、文化、科学、数学、编程等方面的知识。我还可以帮助你完成一些任务,比如编写代码片段、解释技术概念或者提供建议。请告诉我你需要什么帮助!
`;
// 初始化输出容器
const output = document.getElementById('output');
// 开始模拟打字效果
simulateChatGPTTyping(output, text);

模块化组件

ChatGPT能生成很多种类的结果,包括:文字、列表、表格、代码等等,那么前端如何对应展示呢❓这里只讲下实现思路

其中最简单的一种方案就是把后端返回的markdown格式的数据直接喂给页面上的markdown组件,只需要丰富markdown组件功能就行,对格式的解析直接交给其内部

这种方式比较简单,容易大众化无法做好定制功能,要实现界面的定制功能,就要设计到对数据的解析了;需要自研如何解析匹配数据,然后根据不同的类型数据调用不同的组件,这样就可以满足定制功能了,相对来说比较复杂点

实现定制功能需要做很多种的组件,那么就涉及到了前端组件库的设计了,比如:按需加载等等

语音技术

随着互联网技术的发展,页面上也出现了各种各样的富媒体内容,如:音频、视频等等,工作压力下可能有很多读者无法持续关注这方便内容,下面就来看下ChatGPT用到的功能

文字朗读

文字朗读看上去很高大上,很多都支持这个功能,如:攻粽号朗读,ChatGPT也支持

作者博客网站也是加上了文本朗读的功能

实现它非常简单,浏览器提供了speechSynthesis标准来实现文字朗读的功能,基本各大主流浏览器都支持

来简单朗读一段文本:

const synth = window.speechSynthesis;
const text = "我是一段文本,请朗读";
const utterance = new SpeechSynthesisUtterance(text);const voices = synth.getVoices();
const chineseVoice = voices.find((voice) => voice.lang === "zh-CN");
if (chineseVoice) {utterance.voice = chineseVoice; // 设置语言
}
// 朗读
synth.speak(utterance);

除此之外还支持调整音色、声音以及监听各种朗读事件等等

对话

对话相对文本朗读来说更复杂一点,涉及到录音、播放声音等逻辑,整体流程就是录音、识别、播放声音。先来看如何录音

H5提供了MediaRecorder标准API来进行媒体的轻松录音,需要通过调用 MediaRecorder() 构造方法进行实例化。使用之前需要调用MediaDevices.getUserMedia()给予使用媒体输入的许可权限,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道,包括音频、视频

let mediaRecorder: MediaRecorder;
const recordDataChunks: Blob[] = [];function startRecording() {navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {try {mediaRecorder = new MediaRecorder(stream);mediaRecorder.ondataavailable = e => {recordDataChunks.push(e.data);};mediaRecorder.start(1000);state.isRecording = true;mediaRecorder.addEventListener("stop", () => {console.log("录制完成");const blob = new Blob(recordDataChunks, {type: "audio/ogg; codecs=opus",});const url = URL.createObjectURL(blob);console.log(url);});} catch (error) {state.isRecording = false;}});
}

拿到用户的声音后就需要开始识别,然后思考了,最后将内容播放出来就可以了

语音识别

语音识别即音频转文字的功能,ChatGPT在说话时也会将语音实时转化为文字

这个功能使用js还是比较简单的

function transferAudioToText() {// 检查浏览器是否支持 Web Speech APIif (!("webkitSpeechRecognition" in window)) {message.error("你的浏览器不支持语音识别");return;}const recognition = new webkitSpeechRecognition();recognition.continuous = false;recognition.lang = "zh-CN";recognition.interimResults = false;recognition.start();recognition.onresult = (e: any) => {console.log(e.results[0][0].transcript);};
}

当开始识别时就会调用麦克风讲话,然后实时识别语音

音频可视化

ChatGPT在说话时由用图案动画反馈说话状态

H5提供了 AudioContext 接口提供了音频节点的创建和音频处理或解码的执行操作,使用起来也不是很麻烦,但相对前面2者稍微复杂点

其主要逻辑就是拿到音频后通过audiocontext获取音频节点,最后通过canvas画出想要的图案即可

来看下怎么做

// 创建音频上下文
const audioCtx = new AudioContext();
const audioSource = audioCtx.createMediaStreamSource(mediaStream); // 这里把音频媒体流传入
const analyser = audioCtx.createAnalyser();  // 创建音频分析器
audioSource.connect(analyser);  // 将音频连接到分析器
analyser.fftSize = 2048;
audioDataBuffer = new Uint8Array(analyser.frequencyBinCount);// 最后将其渲染到canvas上就可以了
const ctx = canvasRef.value!.getContext("2d")!;
const canvasW = (canvasRef.value!.width = canvasRef.value!.parentElement!.offsetWidth - 48);
const canvasH = (canvasRef.value!.height = 120);
ctx.fillStyle = "#4646fc";function drawAudioTrackBarGraphic() {ctx.clearRect(0, 0, canvasW, canvasH);// 然后通过analyser拿到节点信息analyser!.getByteFrequencyData(audioDataBuffer!);const barLen = audioDataBuffer!.length / 10;const barWidth = canvasW / barLen;for (let i = 0; i < barLen; i++) {const data = audioDataBuffer![i];const barHeight = (data / 255) * canvasH;const x1 = i * barWidth;const y = canvasH - barHeight;ctx.fillRect(x1, y, barWidth, barHeight);}rFId = requestAnimationFrame(drawAudioTrackBarGraphic);
}requestAnimationFrame(drawAudioTrackBarGraphic);

好了,到这里基本上实现了录音、语音转文字、讲话动态反馈效果:

富文本与光标

可以看到ChatGPT的对话框不是简单的textarea标签,而是使用了富文本技术

富文本技术随着技术的进步也发展了多个阶段的产物

  1. document.execCommand命令的最初的简单富文本
  2. contenteditable标签的可编辑dom,开发根据内容自行实现格式展示方式
  3. canvas为主要的自研光标系统,代表为 google docs

而ChatGPT这里简单的对话框则使用了contenteditable=true标签,然后通过内容根据浏览器光标API getSelection 来实现内容的富文本化,其实现还是有一点点复杂的,关键还是内容解析和标签处理

安全防范

好的产品和应用一定少不了安全方面的防范,对于web 应用基本上和我之前的文章 HTTP协议及安全防范 中讲的安全知识大差不差

来看看ChatGPT怎么做的❓

首先就是CSP防范XSS攻击

还有禁止客户端的 MIME 类型嗅探行为,通知浏览器应该只通过 HTTPS 访问该站点等等

总之,万变不离其宗。读者可以翻阅往期文章

性能优化

性能方面的手段也值得读者学习

缓存

常见的HTTP缓存,以及indexDB数据库使用等等,这里不再讲了

Server Push

利用HTTP2的服务器主动推送功能,加快资源的加载,这在HTTP文中也讲过了

TailwindCSS/UnoCSS

Tailwind CSS和UnoCSS都是用于快速构建用户界面的CSS工具,还有读者不了解的需要抓紧看看了

总结

可以看出一个ChatGPT聊天应用虽然看起来非常简单,但背后的逻辑思维非常复杂,涉及到很多复杂的技术,没有一个团队是很难做好的

image

由于图片和格式解析问题,可前往 阅读原文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/883098.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OTA软件升级管理系统

OTA(空中下载技术)是通过空中下载的方式对车辆中的软件进行远程升级。经纬恒润OTA软件升级管理系统基于软件架构、应用架构、业务架构和技术架构,为整车提供云-管-端-屏完整OTA解决方案,可靠地完成系统更新、软件升级、功能迭代和安全漏洞修复等功能。同时支持固件升级(FOTA…

深度学习经典 - 鱼书 - 《深度学习入门:基于Python的理论与实现》 - PDF免费下载

深度学习经典“鱼书”,下载地址:https://pdfs.top/book/深度学习入门:基于Python的理论与实现.html。本书深入浅出地介绍了深度学习的原理,使用Python3从零开始构建深度学习模型。书中详细讲解了神经网络、误差反向传播法、卷积神经网络等核心技术,并探讨了深度学习在自动…

DeepSeek 相关知识学习和整理ing...

【硬件相关】 HBM3e:HBM3e是HBM(高带宽内存)技术的迭代升级版本,属于HBM3的扩展。它采用3D堆叠封装技术,将多个DRAM芯片垂直堆叠,通过硅互连通道传输数据,大幅提升带宽和容量,同时降低功耗。 迭代背景:HBM系列从第一代(HBM)到第五代(HBM3e)持续升级,每一代都提高…

打靶记录27——Tre

靶机: https://www.vulnhub.com/entry/tre-1,483/ 下载(镜像):https://download.vulnhub.com/tre/Tre.zip 难度:中目标:获得 Root 权限 + Flag攻击方法:主机发现 端口扫描 信息收集 进阶路径枚举 EXP 代码改造 突破边界方法1 突破边界方法2 突破边界方法3 权限提升主机发…

7. Docker 容器数据卷的使用(超详细的讲解说明)

7. Docker 容器数据卷的使用(超详细的讲解说明) @目录7. Docker 容器数据卷的使用(超详细的讲解说明)1. Docker容器数据卷概述2. Docker 容器数据卷的使用演示:2.1 宿主 和 容器之间映射添加容器卷2.2 容器数据卷 读写规则映射添加说明2.3 容器数据卷的继承和共享3. 最后:坑:…

到底值不值得本地部署残血版DeepSeek?一文说清!教你如何白嫖满血版DeepSeek

一、介绍最近一段时间,DeepSeek 备受关注,夏天也向身边朋友推荐。但它常无法使用,原因是受到大规模恶意攻击,且 IP 地址在美国。 通过以下网站可以查看DeepSeek网站状态: status.deepseek.com/ 可以发现,最近标红的就是故障中​若遇服务器繁忙提示,大概率是被攻击了,并…

干货:DeepSeek+SpringAI实现流式对话!

前面一篇文章我们实现了《炸裂:SpringAI内置DeepSeek啦!》,但是大模型的响应速度通常是很慢的,为了避免用户用户能够耐心等待输出的结果,我们通常会使用流式输出一点点将结果输出给用户。 那么问题来了,想要实现流式结果输出,后端和前端要如何配合?后端要使用什么技术实…

Ftrans文件安全外发系统,为企业数据保驾护航!

随着企业的不断发展,集团分公司及各部门需向外部客户、合作伙伴及海外同事外发文件。过去,主要通过邮件、FTP方式将数据进行外发,主要存在以下问题和挑战: 1.进行文件外发时需通过OA的审批,由于OA审批与FTP传输两个环节割裂,公司无法有效限制数据外发范围和管控数据外发安…

本地部署 DeepSeek:小白也能轻松搞定!

大家好,我是晓凡。 写在前面 最近DeepSeek太火了,以至于每个小伙伴都想试试。DeepSeek 的到来可谓是开启了全民AI热潮。 本以为DeepSeek本地化部署有多难,实际上验证后很简单,操作起来就像给电脑装个新软件那么简单,大约十多分钟可完成本地部署。 今天咱们来聊聊如何在自己…

Git指南-从入门到精通

代码提交和同步命令 流程图如下:第零步: 工作区与仓库保持一致 第一步: 文件增删改,变为已修改状态 第二步: git add ,变为已暂存状态$ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ git add xx/xx.py xx/xx2.py # 添加某几个…

一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019

一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019不熟悉整个流程的朋友可以先看之前的部署文章,手动部署一遍 一步步教你在CentOS 7.9上安装SQL Server 2019前言 这套Ansible脚本属于红帽官方出品,是一套mssql的自动化运维脚本,能够实现mssql的单实例部署和Alwa…

【Linux】Linux如何查看JDK的安装路径

如何在一台Linux服务器上查找JDK的安装路径呢?有那些方法可以查找定位JDK的安装路径?是否有一些局限性呢?下面总结了一下如何查找JDK安装路径的方法. 1、echo $JAVA_HOME 使用$JAVA_HOME的话能定位JDK的安装路径的前提是配置了环境变量$JAVA_HOME,否则如下所示,根本定位不…