前端需要接收后端的流式返回数据,并实时渲染。
普通的xhr请求都是等http协议数据包一次性返回之后才渲染,类似于ChatGPT的Http接口内容类型为text/event-stream。这种内容类型需要与浏览器建立持久连接并持续监听服务器返回的数据。npm 方式安装类库
npm install @microsoft/fetch-event-source
- 使用
let controller = new AbortController()const eventSource = fetchEventSource(fetchUrl, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(params),signal: controller.signal,onopen() {console.log('open')},onmessage(event) {console.log('onMessage',event.data)let data = event.datalet jsonData = JSON.parse(data);},onclose() {controller.abort();//出错后不要重试eventSource.close();},onerror(error) {console.log('close',error)controller.abort();//出错后不要重试eventSource.close();}})}
调用 fetchEventSource API可以返回一个对象,这个对象可以控制在连接失败时不要重试,直接关闭本次连接。
- onopen:建立连接的回调
- onmessage:接收一次数据段时回调,因为是流式返回,所以这个回调会被调用多次。
- onclose:正常结束的回调
- onerror:连接出现异常回调
onmessage的主要逻辑就是累加所有流式数据返回的内容,然后渲染到页面上,这里需要注意的是接口返回的为markdown格式的数据,所以还需要用到一个markdown转html的依赖库。
- 光标闪烁效果模拟
这里其实是用了一个span来模拟实现的
const cursorFlaskStr = "|"setInterval(() => {let cursorFlask = document.getElementById("cursorFlask");if (cursorFlask) {let opacity = cursorFlask.style.opacity;if (opacity && opacity.trim() == '0') {cursorFlask.style.opacity = '1'} else {cursorFlask.style.opacity = '0'}}}, 245)
这里的逻辑是每次监听到后端的流式数据以后,动态拼接上已有的字符串,然后将markdown转为html格式,然后继续拼接上这个span标签一起渲染到页面上,这个时候再开启个定时器,定时器获取到这个标签,动态的调整这个标签的opcatity透明度就可以实现光标闪烁的效果了。