当前位置:首页 > 每日看点

为什么招聘高级前端开发这么难?

卡卷网1年前 (2025-03-03)每日看点225

在现代前端开发中,使用Serve-Sent Events(SSE)实现流式数据传输((如聊天消息、实时日志、AI 生成文本的逐字输出等))越来越流行,本文来介绍一个前端如何实现接受数据,解析数据,展示到页面,有很好的用户体验。

一、什么是Serve-Sent Events(SSE)?

@microsoft/fetch-event-source 是一个由微软开发的 JavaScript 库,旨在提供更灵活、功能更强大的服务器发送事件(Server-Sent Events, SSE)。它结合了浏览器原生的 fetch API 和 EventSource 的特性,允许开发者通过 HTTP 流(HTTP Streaming)实现实时数据传输,同时支持更多自定义配置(如请求头、身份认证、错误重试等)。

  • 回调方法:
字段含义
method请求方法(POST、GET)
headers请求头Record<string, string>,通常需要指定'Content-Type': 'application/json','Accept': 'text/event-stream'
body请求的参数
onopen响应回来的回调
onmessage接收到消息的回调,每当服务器发一条消息,就触发接受一条消息
onclose响应结束的回调
onerror响应失败报错的回调
  • 对比原生 API 的优势
特性@microsoft/fetch-event-source原生 EventSource
HTTP 方法支持 GET/POST/PUT 等仅 GET
自定义请求头
请求体支持任意数据(如 JSON)不支持
错误重试可配置的重试逻辑有限的重试
流控制可手动暂停/恢复不支持
页面隐藏时行为可配置是否保持连接默认暂停

适用场景推荐

  • 需要与需要认证的 SSE 服务通信(如传递 Authorization 头)。
  • 使用 POST 请求传递参数并接收流式响应(如 OpenAI 的流式 API)。
  • 需要更健壮的连接管理和错误恢复机制。

二、如何使用@microsoft/fetch-event-source

首先,安装库 npm install @microsoft/fetch-event-source

安装成功,主要的基本用法

import { fetchEventSource } from '@microsoft/fetch-event-source'; async function startStream() { await fetchEventSource('request url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN', }, body: JSON.stringify({ query: "Hello" }), onopen(response) { // 连接成功时触发 if (response.ok) return; throw new Error('连接失败'); }, onmessage(event) { // 接收服务器发送的每条事件 console.log('收到数据:', event.data); // 请求完成 console.log('请求结束标记', data.done) }, onclose() { // 连接关闭时触发 console.log('连接终止'); }, onerror(err) { // 错误处理(默认会抛出异常并自动重试) console.error('错误:', err); throw err; // 抛出错误会触发重试机制 } }); }

三、在angular中实现,具体方案

1、安装库 npm install @microsoft/fetch-event-source

2、新建一个新的服务文件来处理 SSE 请求(chat.service.ts文件)

import { Injectable } from '@angular/core'; import { fetchEventSource } from '@microsoft/fetch-event-source'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ChatService { streamGptResponse(prompt: string): Observable<string> { return new Observable(observer => { let fullResponse = ''; const ctrl = new AbortController(); fetchEventSource('request url', { method: 'POST', headers: { 'Content-Type': 'application/json', // 告诉服务器,客户端数据格式 'Accept': 'text/event-stream' // 客户端声明:希望接收事件流格式的响应 }, body: JSON.stringify({ prompt }), signal: ctrl.signal, onmessage(event) { // 处理每个数据块 try { const data = JSON.parse(event.data); // data可以根据后端返回的具体格式,进行相应的处理 if (data.content) { fullResponse += data.content; observer.next(fullResponse); } // 标记请求完成 if (data.done) { observer.complete(); } } catch (error) { console.error('Parse message failed:', error); } }, onopen(response) { if (response.ok && response.headers.get('content-type')?.includes('text/event-stream')) { console.log('Connection opened'); } else { throw new Error(`Failed to open connection: ${response.status}`); } }, onclose() { console.log('Connection closed'); observer.complete(); }, onerror(error) { console.error('Connection error:', error); observer.error(error); ctrl.abort(); } }); // 返回清理函数 return () => { ctrl.abort(); }; }); } }

3、创建一个组件使用该服务

安装markdown,解析样式: npm i ngx-markdown

推荐使用14.0.1版本,markdown的使用下一篇文章会解释

import { Component } from '@angular/core'; import { SseService } from '../../services/sse.service'; @Component({ selector: 'app-gpt-stream', template: ` <div class="gpt-stream-container"> <div class="input-area"> <textarea [(ngModel)]="prompt" placeholder="请输入..." ></textarea> <button (click)="generateResponse()" [disabled]="isGenerating" > 发送 </button> </div> <div class="response-area"> <markdown *ngIf="response" [data]="response"></markdown> <div *ngIf="isGenerating" class="loading"> 思考中... </div> </div> </div> `, styles: [` .gpt-stream-container { padding: 20px; } .input-area { margin-bottom: 20px; } textarea { width: 100%; min-height: 100px; padding: 10px; margin-bottom: 10px; } .response-area { padding: 15px; border: 1px solid #eee; border-radius: 4px; min-height: 100px; } .loading { color: #666; font-style: italic; } `] }) export class GptStreamComponent { prompt = ''; response = ''; isGenerating = false; constructor(private sseService: SseService) {} generateResponse() { if (!this.prompt.trim() || this.isGenerating) { return; } this.isGenerating = true; this.response = ''; this.sseService.streamGptResponse(this.prompt) .subscribe({ next: (chunk) => { this.response = chunk; }, error: (error) => { console.error('Stream error:', error); this.isGenerating = false; }, complete: () => { this.isGenerating = false; } }); } }

3、在 module 文件中注册组件和服务:

import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MarkdownModule } from 'ngx-markdown'; import { GptStreamComponent } from './components/gpt-stream/gpt-stream.component'; import { SseService } from './services/sse.service'; @NgModule({ declarations: [ GptStreamComponent ], imports: [ CommonModule, FormsModule, MarkdownModule.forRoot() ], exports: [ GptStreamComponent ], providers: [ SseService ] }) export class SharedModule { }

六、总结

主要实现特点:

1. 流式处理:

  • 使用 fetchEventSource 建立 SSE 连接
  • 通过 Observable 包装事件流
  • 实时更新 UI 显示生成的内容

2. 错误处理:

  • 包含完整的错误处理机制
  • 连接错误自动中断
  • 解析错误的容错处理

3. 资源清理:

  • 使用 AbortController 控制连接
  • Observable 完成时自动清理
  • 组件销毁时中断连接

4. 用户体验:

  • 显示加载状态
  • 防止重复提交
  • 实时显示生成内容

5. 类型安全:

  • 使用 TypeScript 类型
  • 接口定义清晰
  • 错误类型处理
原文链接:juejin.cn/post/74768813

扫描二维码推送至手机访问。

版权声明:本文由卡卷网发布,如需转载请注明出处。

本文链接:https://www.kajuan.net/ttnews/2025/03/11506.html

分享给朋友:

相关文章

学了两个月网络安全,一直未入门怎么办,知乎大佬们给些建议吧?

学了两个月网络安全,一直未入门怎么办,知乎大佬们给些建议吧?

你才学两个月,就想入门,笑不活了,我学了8年了,还被人说是个菜鸟。 我学了这么多的渗透,依然挖不到自己的漏洞。 ailx10:学习CSRF漏洞 ailx10:学习PHP XXE漏洞 ailx10:学习CORS漏洞 ailx10:学习Cl…

OZON平台什么产品好卖?

ozon选品的核心重点我不说 你们全网也不见说的这么干的教学了 你信我就按照我说的思路去走 不信的出去买课去 一时间消化不了的先点赞收藏起来 真不中了下载也行 因为最近总有坏人给我使诈 平台已经下了我八个视频了 还都是实操教学不废话的那种…

b站真的能自学PS吗?

b站真的能自学PS吗?

看你想达到哪一种程度了,如果你只是平常用PS扣图、调整照片大小、尺寸、简单调个色这样,自学真的挺简单的,B站很多免费的教程都可以教会你这些技巧。 但是如果说你想成为专业的设计师或者是商业修图师,无师自通真的非常难,首先你会走很多弯路,不知道…

手机用久了,垃圾都在哪里,总是内存显示不够,还很卡,这可怎么解决?

手机用久了,垃圾都在哪里,总是内存显示不够,还很卡,这可怎么解决?

大家的手机在使用一段时间之后啊,是不是都会出现又卡又慢的情况,尤其是安卓手机,这种现象更是非常明显,而且很多朋友啊,也都知道手机之所以会出现这些问题,一般都是手机安装了大量软件,而这些软件在使用过程中会产生大量的缓存垃圾,因此啊时间久了就会…

为什么微服务一定要有网关?

为什么微服务一定要有网关?

网关 一句话总结,网关的作用是上浮公共逻辑,下沉差异逻辑。公共逻辑就是所有接口都需要做的事,比如权限校验,限流算法等,这样业务就只需要关心业务逻辑即可。下面是一个对比图: 当然除了一些公共逻辑外, 路由也是网关的核心功能,它可以进行流量…

腾讯文档回收站彻底删除文件真的找不回来了吗?

趁早打电话联系腾讯文档的人可能还有救,一般这种都是数据库里标记为删除,文件还没有实际删除,然后经过一段时间后程序统一进行真删除。这个“一段时间”可长可短,可能是一小时也可能是几天几个月甚至几年,要看腾讯服务器的程序是怎么写的。 不过你联系腾…

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。