【人工智能】深入解析!三种实现ChatGPT打字机效果的最佳方案

在当今AI快速发展的时代,ChatGPT 凭借其强大的自然语言处理能力,已经成为众多开发者和企业的首选工具。然而,如何在前端页面中实现类似于ChatGPT的打字机效果,以提升用户交互体验,成为了一个广受关注的话题。今天,我将为大家深入解析三种实现ChatGPT打字机效果的最佳方案,并分享一些实战中的踩坑经验,助你轻松应对开发中的各种挑战。🎯

文章目录

    • 技术背景
      • 本文所用示例涉及的技术栈
    • 实现方案概览
    • 方案一:普通请求 + 前端模拟
      • 描述
      • 核心代码解析
      • 代码详解
      • 效果展示
      • 优缺点分析
    • 方案二:基于SSE技术的请求实现
      • 描述
      • 核心代码解析
      • 代码详解
      • 效果展示
      • 优缺点分析
    • 方案三(推荐):Fetch请求 + ReadableStream.getReader()流读取
      • 描述
      • 核心代码解析
      • 代码详解
      • 效果展示
      • 优缺点分析
    • 拓展优化
      • 自定义控制器示例
      • 优化效果
    • 实战中的踩坑分享
      • Umijs开发环境配置
      • Nginx配置
    • 七、更多文献
    • 结语

技术背景

随着人工智能技术的飞速发展,AI交互应用逐渐成为用户与系统之间的重要桥梁。ChatGPT 作为OpenAI推出的先进语言模型,能够生成自然、流畅的文本回复,极大地提升了用户体验。然而,仅有强大的后台逻辑是不够的,前端的表现形式同样关键,特别是那些能够模拟人类打字行为的打字机效果,不仅能增强互动性,还能提升用户的沉浸感。

本次技术探讨将聚焦于如何在Web端实现类似于ChatGPT的打字机效果。我们将介绍三种常见的实现方案,并详细分析每种方案的优缺点,帮助开发者选择最适合自己项目的方案。此外,还将分享一些在Umijs开发环境Nginx配置中遇到的常见问题及解决方案,助力项目顺利推进。🔧

本文所用示例涉及的技术栈

  • 前端框架:React + Tailwindcss
  • 后端框架:Express
  • 其他库
    • @microsoft/fetch-event-source
    • alova.js

提示:本文假设读者已具备上述技术栈的基本使用知识,重点介绍如何结合这些工具实现打字机效果。


实现方案概览

在实现ChatGPT打字机效果的过程中,我们主要探讨以下三种方案

  1. 方案一:普通请求 + 前端模拟

    • 通过常规的Ajax或Fetch请求获取数据,之后在前端通过定时器模拟打字效果。
  2. 方案二:基于SSE技术的请求实现

    • 使用Server-Sent Events (SSE) 技术,实现数据的实时流式传输与展示。
  3. 方案三(推荐):Fetch请求 + ReadableStream.getReader()流读取

    • 利用Fetch APIReadableStream结合,实现更高效的数据流处理,推荐作为主推方案。

接下来,我们将逐一详细介绍每种方案的实现方式、代码示例及其优缺点分析。🔥


方案一:普通请求 + 前端模拟

描述

方案一的核心思想是通过常规的Ajax或Fetch请求一次性获取所有数据,然后在前端通过定时器(如setInterval)逐字展示,实现打字机效果。该方法无需深层次的后端改动,适用于简单的项目或初期开发阶段。🏁

核心代码解析

以下是基于React和Tailwindcss的实现示例:

import { useRef, useState } from 'react';const SSEOnlyFE = () => {const [data, setData] = useState('');const timer = useRef(null)const handleClick = () => {setData('思考中...')clearTimer()fetch('http://localhost:5000/sse').then(response => {if (!response.ok) {throw new Error('Network response was not ok');}return response.text();}).then(resData => {setData('')const rst = filterData(resData)timerEffect(rst)})}const filterData = (dataString) => {let rst = ''const dataBlocks = dataString.split('data:');// 过滤掉第一个空项(由于split()在字符串开始处不匹配)dataBlocks.shift();// 遍历每个数据块,解析JSON并提取contentdataBlocks.forEach(block => {const jsonData = JSON.parse(block);// 根据 event 来整合最终的数据if (jsonData.event === 'start' || jsonData.event === 'message') {rst += jsonData.content}// 因为当前方案的 fetch 请求获取的是 SSE 连接结束后的整体数据,因此不必在意 jsonData.event 为 done 的状态});return rst}// 用 setInterval 来实现逐个字符的输出const timerEffect = (contentStr) => {const contentList = contentStr.split('')timer.current = setInterval(() => {if (contentList.length > 0) {const content = contentList.shift()setData(prevData => prevData + content);} else {clearTimer()}}, 200)}const clearTimer = () => {if (timer.current) {clearInterval(timer.current)}timer.current = null}return <div className='m-[20px] ml-[40px]'><h2 className='mb-[10px] text-[20px] font-bold'>普通请求 + 前端模拟</h2><div className=' p-[10px] w-[400px] h-[200px] bg-slate-200'><div className='w-[80px] h-[30px]  text-center rounded-[10px] bg-blue-300 cursor-pointer' onClick={handleClick}><span>发起请求</span></div><div><div>输出结果:</div><div className='text-[#333] w-[300px] '>{data}</div></div></div></div>
};export default SSEOnlyFE;

代码详解

  1. 状态管理

    • 使用React的useState管理显示数据。
    • 使用useRef管理定时器的引用,以便在需要时清除定时器。
  2. 发起请求

    • 用户点击按钮后,触发handleClick函数,开始数据请求。
    • 通过fetch请求后端的SSE接口,获取完整的数据流。
  3. 数据处理

    • 使用filterData函数解析响应数据,提取实际内容。
    • 将解析后的完整字符串传递给timerEffect函数,开始逐字符显示。
  4. 打字机效果实现

    • timerEffect函数利用setInterval定时逐个字符地更新显示内容。
    • 当所有字符展示完毕后,清除定时器,结束效果。

效果展示

效果如下所示,通过点击“发起请求”按钮,数据将以打字机效果逐字符展示:

在这里插入图片描述

优缺点分析

优点

  • 简单易行:无需复杂的后端改动,只需前端简单处理即可实现打字机效果。
  • 适用范围广:适用于数据量较小、响应速度快的场景,开发周期短。
  • 低改造成本:对于已有项目,只需在前端添加定时器逻辑,无需调整后端接口。

缺点

  • 用户体验较差:当数据量大或后端响应缓慢时,整个接口请求耗时较长,导致打字效果延迟,影响用户体验。
  • 资源浪费:前端通过定时器逐字符显示,存在性能开销,特别是在高频操作下,可能导致页面卡顿。
  • 无法实时更新:由于一次性获取所有数据,无法实现数据的实时流式展示,缺乏动态性。

建议

适用于问答较少、数据量较小的项目初期阶段,作为临时方案使用。一旦项目规模扩大,建议立即迁移至更高效的SSEReadableStream方案,以提升性能和用户体验。🚀


方案二:基于SSE技术的请求实现

描述

方案二利用Server-Sent Events (SSE)技术,通过浏览器原生的EventSource对象,实现数据的实时流式传输。与方案一相比,SSE能够更高效地处理数据流,减少前端处理负担,提升用户体验。💡

快速体验中文版GPT - ChatMoss & ChatGPT中文版

核心代码解析

以下是基于React和alova.js库的实现示例:

import React, { useRef, useEffect, useState } from 'react';
import { useSSE } from '@alova/scene-react';
import { createAlova } from 'alova';
import GlobalFetch from 'alova/GlobalFetch';const alovaInstance = createAlova({requestAdapter: GlobalFetch()
});const SSEAlova = () => {// 此处是 alova 库的用法,详情请参考相关文档const method = (value) => alovaInstance.Get('http://localhost:5000/sse', { param: { key: value } });const { data, send, close } = useSSE(method, {immediate: false,});const [value, setValue] = useState('');useEffect(() => {if (data) {try {const jsonData = JSON.parse(data);if (jsonData.event === 'start' || jsonData.event === 'message') {setValue(prevData => prevData + jsonData.content);} else if (jsonData.event === 'done') {// 当消息发送完毕时,接收到 done 的事件,则前端主动关闭,否则会持续获取消息close()}} catch (err) {console.log(err)close()}}}, [data])const handleClick = () => {setValue('')send('begin')}return <div className='m-[20px] ml-[40px]'><h2 className='mb-[10px] text-[20px] font-bold'>SSE请求 + Alova.js</h2><div className=' p-[10px] w-[400px] h-[200px] bg-slate-200'><div className='w-[80px] h-[30px]  text-center rounded-[10px] bg-blue-300 cursor-pointer' onClick={handleClick}><span>发起请求</span></div><div><div>输出结果:</div><div className='text-[#333] w-[300px] '>{value}</div></div></div></div>
};export default SSEAlova;

代码详解

  1. 使用Alova.js库

    • alova.js 是一个封装了SSE请求的现代化网络请求库,简化了SSE的使用流程。
    • 通过createAlova创建实例,并使用useSSE钩子进行SSE请求的管理。
  2. 状态管理

    • 使用React的useState管理显示的数据内容。
    • useEffect监听数据变化,实时更新显示内容。
  3. 发起请求

    • 用户点击按钮后,触发handleClick函数,发送SSE请求。
    • send方法启动SSE连接,close方法关闭连接。
  4. 数据处理

    • useEffect中解析收到的数据,根据事件类型 (start, message, done) 更新显示内容。
    • 在接收到done事件后,主动关闭SSE连接,避免持续监听。

效果展示

通过点击“发起请求”按钮,数据将实时流式展示,效果如下:

在这里插入图片描述

优缺点分析

优点

  • 实时性强:SSE技术能够实时推送数据,用户无需等待全部数据加载完成,即可开始浏览信息。
  • 减少前端负担:通过alova.js等库的封装,简化了SSE的使用,减少了前端逻辑处理。
  • 高效资源利用:无需频繁发起请求,减少服务器资源消耗,提升应用性能。

缺点

  • 无法设置Header参数:SSE的EventSource对象不支持自定义请求头,限制了在请求中添加认证信息或其他自定义头部。
  • 兼容性问题:部分老旧浏览器或特殊环境下,SSE可能无法正常工作,需要考虑兼容性。
  • 服务端处理复杂:需要服务端支持SSE协议,确保数据的正确流式传输,增加了后端实现的复杂度。

建议

适用于新项目,且服务端能够灵活处理SSE请求的场景。若项目需要在请求中添加认证信息或其他自定义头部,需在服务端做额外的处理,如通过URL参数传递认证信息等。此外,建议在项目初期充分测试SSE的兼容性,确保在目标用户环境中正常运行。🌐


方案三(推荐):Fetch请求 + ReadableStream.getReader()流读取

描述

方案三结合了Fetch APIReadableStream,通过fetch请求获取流式数据,并利用ReadableStream.getReader()进行逐步读取。这种方式不仅突破了SSE在请求头设置上的限制,还能更灵活地控制数据流,提升用户体验。由于其高度的可定制性和高效性,被推荐作为实现打字机效果的首选方案。🏆

快速体验中文版GPT - ChatMoss & ChatGPT中文版

核心代码解析

以下是基于React和@microsoft/fetch-event-source库的实现示例:

# 体验中文版GPT:https://pc.aihao123.cn/index.html#/page/login?invite=1141439&fromChannel=CodeMoss_1115_dazijiimport React, { useRef, useEffect, useState } from 'react';
import { fetchEventSource } from '@microsoft/fetch-event-source';const SSEFetchEventSource = () => {const [value, setValue] = useState('');const handleClick = () => {setValue('思考中...')fetchEventSource('http://localhost:5000/sse', {headers: {'authorization': 'test sse'},onopen(res) {console.log('连接:', res)setValue('')},onmessage(res) {try {const jsonData = JSON.parse(res.data);if (jsonData.event === 'start' || jsonData.event === 'message') {setValue(prevData => prevData + jsonData.content);} else if (jsonData.event === 'done') {// 因为本质还是 fetch 接口,当消息发送完毕时,// 接收到 done 的事件,如无特殊逻辑可以不做处理,有特殊逻辑可以做其他逻辑处理}} catch (err) {console.log(err)}},onerror(err) {console.log('错误:', err)}})}return <div className='m-[20px] ml-[40px]'><h2 className='mb-[10px] text-[20px] font-bold'>Fetch 请求 + ReadableStream </h2><div className=' p-[10px] w-[400px] h-[200px] bg-slate-200'><div className='w-[80px] h-[30px]  text-center rounded-[10px] bg-blue-300 cursor-pointer' onClick={handleClick}><span>发起请求</span></div><div><div>输出结果:</div><div className='text-[#333] w-[300px] '>{value}</div></div></div></div>
};export default SSEFetchEventSource;

代码详解

  1. 使用@microsoft/fetch-event-source

    • 该库扩展了原生fetch功能,支持SSE风格的数据流读取,同时允许自定义请求头,解决了原生SSE无法设置Header的问题。
  2. 状态管理

    • 使用React的useState管理显示的数据内容。
  3. 发起请求

    • 用户点击按钮后,触发handleClick函数,开始SSE请求。
    • 通过fetchEventSource发起请求,设置自定义请求头(如authorization)。
  4. 数据处理

    • onmessage回调实时接收数据,根据事件类型 (start, message, done) 更新显示内容。
    • onopen回调用于处理连接建立时的逻辑。
    • onerror回调用于处理错误情况,确保连接的稳定性和数据的完整性。

快速体验中文版GPT - ChatMoss & ChatGPT中文版

效果展示

点击“发起请求”按钮,数据将实时流式展示,效果如下:

在这里插入图片描述

优缺点分析

优点

  • 灵活性高:结合Fetch APIReadableStream,实现高度自定义的数据流读取,满足复杂业务需求。
  • 支持自定义请求头:解决了SSE无法设置Header的限制,便于在请求中添加认证信息或其他自定义头部。
  • 实时性强:与SSE类似,能够实时接收并展示数据,提升用户体验。
  • 广泛兼容:基于标准的Fetch APIReadableStream,兼容性良好,适用于大多数现代浏览器。

缺点

  • 实现复杂度较高:相比方案一和方案二,需理解并掌握ReadableStream的使用,增加了开发复杂度。
  • 依赖第三方库:需要引入@microsoft/fetch-event-source等库,增加了项目依赖。
  • 库维护问题@microsoft/fetch-event-source库更新缓慢,遇到问题可能需自行阅读源码或寻找替代方案。

建议

强烈推荐作为实现ChatGPT打字机效果的首选方案。其高效、灵活的特性,尤其是在需要自定义请求头及处理复杂数据流的场景下,表现尤为出色。如果团队具备相关技术背景或愿意投入时间学习ReadableStream,则可以充分利用此方案的优势,打造流畅的用户交互体验。💯


拓展优化

为了进一步提升打字机效果的丝滑度,我们可以在现有方案的基础上,结合前端定时器进行数据流的均匀输出。由于不同大模型返回数据的速度和内容可能存在波动,合理控制数据输出的速率,可以显著提升用户的视觉体验。✨

自定义控制器示例

以下是一个简化版的MessageManager类,用于控制数据的流式输出:

/*** 打字机效果*/
export default class MessageManager {messageList: string[] = [];timer: any = null;timerDelay = 100;onFinish: () => void;onMessage: (message: string) => void;stopFlag = false; // 停止标志,如果设置了停止,但是队列没走完,就会等队列走完之后再停止constructor(messageList: string[],timerDelay: number,onMessage: (message: string) => void,onFinish: () => void,) {this.messageList = messageList;this.timerDelay = timerDelay;this.onFinish = onFinish;this.onMessage = onMessage;}start() {this.timer = setInterval(() => {if (this.messageList.length > 0) {this.consume();} else {if (this.stopFlag) {this.immediatelyStop();}}}, this.timerDelay);}consume() {if (this.messageList.length > 0) {const str = this.messageList.shift();str && this.onMessage(str);}}add(str: string) {if (!str) return;const strChars = str.split('');this.messageList = [...this.messageList, ...strChars];}stop() {this.stopFlag = true;}immediatelyStop() {// 立刻停止clearInterval(this.timer);this.timer = null;this.messageList = [];this.onFinish();}
}

优化效果

通过上述控制器,可以实现更加平滑的打字机效果,确保不同数据块的输出速率保持一致,避免因数据波动导致的展示不均匀。


实战中的踩坑分享

在实际开发过程中,难免会遇到一些意想不到的问题。以下,我将分享在Umijs开发环境配置Nginx配置中遇到的常见问题及解决方案,希望对大家有所帮助。🔧

Umijs开发环境配置

在项目中使用Umijs脚手架,并采用上述方案三时,可能会遇到SSE请求无法实时获取数据,只能在最后一次性展示的问题。经过调试,发现是由于Umijs开发服务器默认启用了压缩中间件,导致SSE数据无法流式传输。解决方法如下:

  1. 关闭压缩中间件
    在启动开发服务器时,添加环境变量UMI_DEV_SERVER_COMPRESS=none,禁用压缩功能。

    UMI_DEV_SERVER_COMPRESS=none umi dev
    
  2. 升级Umijs版本
    确保使用的Umijs版本支持相关配置,推荐升级至4.1.5及以上版本。

注意:此配置仅影响本地开发环境,不会影响生产环境的打包和部署。

Nginx配置

将项目部署到服务器后,可能会发现SSE接口的数据依然无法实时展示,而是等所有数据传输完成后才整体显示。这通常是由于Nginx代理默认启用了缓冲机制。解决方法如下:

  1. 禁用缓冲
    针对SSE接口,关闭Nginx的缓冲功能,确保数据能实时流式传输。

    # 配置 SSE 请求
    location /sse {proxy_pass http://localhost:5000/sse;# 禁用缓冲proxy_buffering off;# 设置必要的SSE头部proxy_set_header Cache-Control 'no-cache';proxy_set_header Connection 'keep-alive';  
    }
    
  2. 优化代理设置
    确保仅对SSE接口进行上述配置,避免对其他接口产生不必要的影响。

提示:进行Nginx配置修改后,记得重启Nginx服务,使配置生效。


快速体验中文版GPT - ChatMoss & ChatGPT中文版

七、更多文献

【VScode】揭秘编程利器:教你如何用“万能@符”提升你的编程效率! 全面解析ChatMoss & ChatGPT中文版

【VScode】VSCode中的智能编程利器,全面揭秘ChatMoss & ChatGPT中文版

结语

通过本文的详细解析与实战指南,相信大家对实现ChatGPT打字机效果的三种方案有了更为深入的理解。总结如下:

  1. 方案一:普通请求 + 前端模拟,实现简单,适用于初期开发或数据量较小的场景,但用户体验和性能表现一般。
  2. 方案二:基于SSE技术的请求实现,实时性强,适用于新项目,但存在请求头设置限制。
  3. 方案三(推荐):Fetch请求 + ReadableStream.getReader()流读取,灵活高效,支持自定义请求头,适用于复杂业务场景,是当前实现打字机效果的最佳选择。

同时,分享的Umijs开发环境配置Nginx配置踩坑经验,希望能帮助大家在实际项目中少走弯路,快速实现预期功能。💪

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++:继承

一、什么是继承&#xff1f; 概念&#xff1a; 在我们认识模板之后&#xff0c;模板是写与类型无关的代码&#xff0c;是一种复用方法。今天讲解的是继承&#xff0c;继承也是代码复用的方法&#xff0c;是在原有的基础上进行增加新的类。由此继承体现了面向对象的层次结构&a…

Java版本Spring Cloud+SpringBoot b2b2c:Java商城实现一件代发设置及多商家直播带货商城搭建

一、产品简介 我们的JAVA版多商家入驻直播带货商城系统是一款全*面的电子商务平台&#xff0c;它允许商家和消费者在一个集成的环境中进行互动。系统采用先进的JAVA语言开发&#xff0c;提供多商家入驻、直播带货、B2B2C等多种功能&#xff0c;帮助用户实现线上线下的无缝对接…

【Linux】进程

目录 谈谈硬件冯诺依曼体系结构数据流向 谈谈软件(操作系统)什么是操作系统&#xff1f;为什么需要操作系统&#xff1f;操作系统如何管理&#xff1f; 谈谈进程管理进程PCB查看进程ps ajxprockill -9 PID 系统调用getpid()getppid()fork() 进程状态linux下的进程状态RSDT/tXZ …

【comfyui教程】ComfyUI绘画|ComfyUI 本地部署(Windows系统)

前言 关于 ComfyUI 的部署&#xff0c;推荐使用 Window系统 英伟达显卡 的搭配组合。 整合包下载⏬ 所有的AI设计工具&#xff0c;安装包、模型和插件&#xff0c;都已经整理好了&#xff0c;&#x1f447;获取~ PS&#xff1a;最好是下载到固态硬盘内&#xff0c;确保存储空…

飞牛云fnOS本地部署1Panel服务器运维管理面板并搭建Halo个人博客

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Python实现贪吃蛇 经典解压小游戏!附源码

大家应该都玩过诺基亚上面的贪吃蛇吧&#xff0c;那是一段美好的童年回忆&#xff0c;本文将带你一步步用python语言实现一个snake小游戏&#xff01; 基础环境必备 版本&#xff1a;Python3 ●系统&#xff1a;Windows ●相关模块&#xff1a;pygame pip install pygame安…

史上最强大的 S3 API?介绍 Prompt API。

迄今为止&#xff0c;对象存储世界已由 PUT 和 GET 的 S3 API 概念定义。然而&#xff0c;我们现在生活的世界需要更多。鉴于 MinIO 的 S3 部署甚至比 Amazon 还多&#xff0c;因此我们不得不提出下一个出色的 S3 API。 这个新 API 就是 Prompt API&#xff0c;它很可能成为有…

微信小程序 — 农产品供销系统

农产品供销系统 一&#xff1a;基本介绍开发环境功能模块图系统功能部分数据库表设计 二&#xff1a;部分系统页面展示小程序登录界面小程序首页水果分类列表 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/415514d6c40c461c91c1739a4f682fea.jpeg#pic_center)小程序…

为什么说数字化转型需要用到RPA

在现代商业环境中&#xff0c;数字化转型已成为企业追求创新、提高竞争力和适应市场变化的重要战略。然而&#xff0c;数字化转型不仅仅是简单地将纸质文档转化为电子文件或引入新的IT系统&#xff0c;而是要全面优化和重塑企业的业务流程、运营模式和客户体验。在这一过程中&a…

海外媒体发稿:聚焦摩洛哥世界新闻 Morocco World News

关于摩洛哥世界新闻简介&#xff1a; 摩洛哥世界新闻&#xff1a;通过卓越的新闻报道倡导言论自由和深思熟虑的辩论 摩洛哥世界新闻致力于向广大受众提供摩洛哥和中东及北非地区的新闻&#xff0c;不带偏见或政治目的。摩洛哥世界新闻的愿景是成为言论自由的捍卫者&#xff0…

快速建造高品质音乐厅:声学气膜馆打造专业降噪空间—轻空间

随着音乐艺术在城市生活中的地位不断提升&#xff0c;各类音乐厅和演出场馆的需求量也逐年增加。然而&#xff0c;传统音乐厅的建设往往周期长、成本高&#xff0c;特别是在城市中心和文化聚集区&#xff0c;土地资源有限&#xff0c;建造优质的音乐厅面临诸多挑战。如何在有限…

Stable Diffusion Web UI - Checkpoint、Lora、Hypernetworks

Checkpoint、Lora、Hypernetworks是 Stable Diffusion Web UI 生图的重要工具&#xff0c;它们有各自的特点&#xff0c;结合不同的生图场景选择一个或者多个叠加使用&#xff0c;能够更好的命令 Stable Diffusion 生成理想状态的图片。 以人像生图用通俗的方式解释checkpoint…

深度学习神经网络热点全解:原理精析与丰富应用场景大揭秘

深度学习神经网络是人工智能领域的重要研究方向&#xff0c;以下是一些热点方向及其原理和具体应用场景&#xff1a; 这些深度学习神经网络的热点领域不断推动着人工智能技术的发展和创新&#xff0c;在各个行业和领域展现出了巨大的应用潜力&#xff0c;同时也面临着一些技术挑…

越南很火的slots游戏投放Google谷歌广告策略

越南很火的slots游戏投放Google谷歌广告策略 越南的slot游戏市场正在借助Google广告代投策略推动增长。随着智能手机的普及和互联网的普及&#xff0c;越南的游戏市场迅速增长&#xff0c;吸引了越来越多的投资者和开发者进入该市场。 在这个竞争激烈的市场中&#xff0c;广告…

构建安全的数据库环境:群晖NAS安装MySQL和phpMyAdmin详细步骤

文章目录 前言1. 安装MySQL2. 安装phpMyAdmin3. 修改User表4. 本地测试连接MySQL5. 安装cpolar内网穿透6. 配置MySQL公网访问地址7. 配置MySQL固定公网地址8. 配置phpMyAdmin公网地址9. 配置phpmyadmin固定公网地址 前言 本文将详细讲解如何在群晖NAS上安装MySQL及其数据库管理…

多边形Newell向量

多边形Newell向量 空间平面多边形的Newell向量可以用来表示一个该多边形所在平面的法向量而且 Newell向量的模是这个多边形的面积。 Code 计算多边形的法向量和中心点。 template<typename T> void PolygonTriangulation::ComputePolygonPlane(const TArray<TVect…

【9687】基于springboot+vue的在线考试系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取免费源码 项目描述 21世纪&#xff0c;我国就已普及互联网信息&#x…

出行如何用远程控制软件实现异地办公?

远程办公软件让你轻松享受假期&#xff01; 想象一下&#xff0c;像类似于国庆长假里&#xff0c;你在海边享受着悠闲时光&#xff0c;突然工作上有点急事需要处理。这时&#xff0c;如果你有一款好用的远程办公软件&#xff0c;一切就变得轻松多了。今天&#xff0c;我们就来…

力扣-Mysql-3252-英超积分榜排名 II(中等)

一、题目来源 3252. 英超积分榜排名 II - 力扣&#xff08;LeetCode&#xff09; 二、数据表结构 表&#xff1a;TeamStats --------------------------- | Column Name | Type | --------------------------- | team_id | int | | team_name | v…

用Python设置PowerPoint幻灯片背景

使用Python自动化处理Office文档&#xff0c;如PowerPoint演示文稿&#xff0c;是提高效率和创造力的重要手段。设置PowerPoint幻灯片背景不仅能够增强演示文稿的视觉吸引力&#xff0c;还能帮助传达特定的情感或信息&#xff0c;使观众更加投入。通过编程方式批量修改幻灯片背…