不止WebSocket可以实现长连接,它也可以

点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

前言

首先听到服务端消息推送一般大多数可能都是使用到的websocket,但是websocket一般主要是用于聊天室,而SSE(Server-Sent Events)也是可以基于服务器来与Web页面来进行数据和消息的推送的,但是与websocket不同的是,服务器发送事件是单项的,数据只能从服务端发送到客户端。例如我们可以看一下常用的chatGtp他的文本输出就是基于服务器的消息推送进行输出的。下面我们来看如何来建立一个通讯连接。

EventSource

EventSource是一个实例,专门用来建立与服务器的连接接收服务器的消息推送的,他会与服务器建立一个HTTP的长连接,他会一致保持开启,直到调用close关闭连接。ps:他是无法使用axios的因为axios内部其实使用的是XMLHttpRequest,他是无法支持接收服务端推送的消息的。

我们先实现一个简单的后台服务,然后前台来使用这个实例看一下效果是怎么样的。

const article = `警告:当不使用 HTTP/2 时.....。`
app.get('/chat_typing', (req, res) => {
  // 开启 Server-sent events
  res.setHeader('Content-Type', 'text/event-stream')
  let index = 0
  let timerId = 0
  // 模拟每隔 0.1s 向前端推送一次
  timerId = setInterval(() => {
    // 获取文字
    const data = article[index]
    // 下标累加
    index++
    // 响应结果
    if (data) {
      // data:表示数据内容,\n\n 表示结尾。
      res.write(`data: ${data}\n\n`)
    } else {
      res.end()
      clearInterval(timerId)
    }
  }, 100)
})

这里我们简单实现了一个node服务,主要的就是Content-Type这里我们把他设置为了text/event-stream,也就是服务端的消息推送,然后我们每100毫秒推送一个字符过去,然后我们再前台看一下接收的效果是什么样子的

<script setup>
import { ref } from 'vue'

const article = ref('')
let source
const OpenSSE = () => {
  source = new EventSource('http://localhost:3000/chat_typing')
  // 接收信息
  source.addEventListener('message', (e) => {
    // 实时输出字符串
    article.value += e.data
  })
}
</script>

<template>
  <div>
    <div>
      <button @click="OpenSSE">开启SSE</button>
    </div>
    <div>{{ article }}</div>
  </div>
</template>

前台内我们就是声明了一个EventSource实例,然后点击开启的时候监听他的message,拿到服务器推送过来的消息,进行组装展示。就实现了如下的效果

435f87b377e02b8c41bd3504d09fdc07.jpeg 这里我们可以看到他确实是开启了一个长连接,一直在接收服务器推送过来的数据,且请求类型也变成了eventsource,我们在某些情况下需要关闭这个连接只需要调用实例方法内的close方法即可对其进行关闭

const CloseSSE = () => {
  source.close()
}

优化连接

EventSource虽然可以让我们实现一些简单场景下的服务器消息推送的接收,但是他只支持get请求,并且只能通过路径拼接的方式进行参数的携带。但是我们如果遇到像chat一样会携带上下文进行数据传输的时候那么就不太适合了,因为处理起来比较麻烦,并且有着一定长度的限制。那么我们就可以使用fetch来进行连接,下面我们看一下如何使用fetch来建立连接获取服务器推送过来的消息。

const OpenSSE = async () => {
  const res = await fetch('http://localhost:3000/chat_typing')
  console.log(res)
}

这里我们呢就实现了连接,那么如何去停呢,我们需要使用到 AbortController来终止请求

const OpenSSE = async () => {
  abort = new AbortController()
  const res = await fetch('http://localhost:3000/chat_typing', {
    signal: abort.signal,
  })
  console.log(res)
}
const CloseSSE = () => {
  abort.abort()
}

abort可以用来终止一个未完成的异步操作,可以用来终止所有的响应和流。正好我们现在使用fetch返回过来的就是一个流,那么解决了请求和终止操作,接下来就需要解析返回的流了

解析流

我们首先打印res.body可以看到内部存在一个方法,getReader,他呢就是用来获取流数据的,我们是用res.body.getReader可以获取到一个一个Promise,那么我们就可以进行执行拿到Promise的值。

const res = await fetch('http://localhost:3000/chat_typing', {
    signal: abort.signal,
  })
  const content = res.body.getReader()
  const decode = new TextDecoder()
  while (content) {
    const { done, value } = await content.read()
    if (done) break
    console.log(done, decode.decode(value))
  }

这里呢我们逐步读取流,content.read()是一个异步操作,从流中读取下一块数据。它返回一个包含两个属性的对象,donevalue分别是读取到的值和是否读取完毕,当读取完毕我们直接结束循环,但是此刻的value还是一个Uint8Array的数据格式,我们需要使用TextDecoder进行转换为字符串,到这里就可以拿到数据流推过来的东西了。此刻我们可以再修改一下后台,修改为post请求来实验一下,并且可以直接返回要推送的值。

6786b3b7eedf398f47761c9bea2e3d4e.jpeg


app.post('/chat_typing')
res.write({data})
 const res = await fetch('http://localhost:3000/chat_typing', {
    method: 'POST',
    signal: abort.signal,
  })

这样我们也就实现了可以使用fetch并且写道参数来读取数据流了,并且使用到了POST请求

结尾

这里呢主要介绍的就是如何与后台进行连接实现消息推送,并且传递参数接收数据流,像这样的消息推送一般都是用来消息订阅,消息推送,提醒等功能上。

本文来源于稀土掘金技术社区,作者_onlooker

原文链接:https://juejin.cn/post/7411032557075103759

f49d3f4ab8a364cfb99c9bcada2be4f9.png

往期推荐

高级前端应该掌握的Nginx相关知识

898674aa82a8bc33cf92126d0c423dea.png

性能优化思路之网络层优化

00cb5ec637ad03e13c916deeaeb267d3.png

一个新的 HTML 元素:<permission>!

40c9a35f61aed68f1ca7e67b1872f98e.png


最后

  • 欢迎加我微信,拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

c9a2dbe5ccf5967688e8bc5c7273a64a.jpeg

63ce36d00981f1adb652b49858efa921.png

点个在看支持我吧

44f85a396ed056a3c74b5f0f13acacc9.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值