前言
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~
在日常的开发中,我们经常能碰见服务端需要主动推送给客户端数据的业务场景,比如_数据大屏的实时数据_,比如_消息中心的未读消息_,比如_聊天功能_等等。
本文主要介绍SSE的使用场景和如何使用SSE。
image.png我们常规实现这些需求的方案有以下三种
在很久很久以前,前端一般使用轮询来进行服务端向客户端进行消息的_伪推送_,为什么说轮询是伪推送?因为_轮询本质上还是通过客户端向服务端发起一个单项传输的请求_,服务端对这个请求做出响应而已。通过不断的请求来实现服务端向客户端推送数据的错觉。并不是服务端主动向客户端推送数据。显然,轮询一定是上述三个方法里最下策的决定。
轮询的缺点:
websocket是一个双向通讯的协议,他的优点是,可以同时支持客户端和服务端彼此相互进行通讯。功能上很强大。
缺点也很明显,websocket是一个新的协议,ws/wss。也就是说,支持http协议的浏览器不一定支持ws协议。
相较于SSE来说,websocket因为功能更强大。结构更复杂。所以相对比较_重_。
websocket对于各大浏览器的兼容性↓
sse是一个单向通讯的协议也是一个_长链接_,它只能支持服务端主动向客户端推送数据,但是无法让客户端向服务端推送消息。
长链接是一种HTTP/1.1的持久连接技术,它允许客户端和服务器在一次TCP连接上进行多个HTTP请求和响应,而不必为每个请求/响应建立和断开一个新的连接。长连接有助于减少服务器的负载和提高性能。
SSE的优点是,它是一个轻量级的协议,相对于websockte来说,他的复杂度就没有那么高,_相对于客户端的消耗也比较少_。而且_SSE使用的是http协议_(websocket使用的是ws协议),也就是现有的服务端都支持SSE,无需像websocket一样需要服务端提供额外的支持。
注意:IE大魔王不支持SSE
SSE对于各大浏览器的兼容性↓
注意哦,上图是SSE对于浏览器的兼容不是对于服务端的兼容。
对于当前计算机的发展来说,几乎很少出现同时不支持websocket和sse的情况,所以轮询是在极端情况下浏览器实在是不支持websocket和see的下策。
我们一般的服务端和客户端的通讯基本上使用这两个方案。首先声明:这两个方案没有绝对的好坏,只有在不同的业务场景下更好的选择。
SSE的官方对于SSE和Websocket的评价是
对于SSE来说,它的_优点就是轻_,而且对于服务端的支持度要更好。换言之,_可以使用SSE完成的功能需求,没有必要使用更重更复杂的websocket。_
比如:数据大屏的实时数据,消息中心的消息推送等一系列只需要服务端单方面推送而_不需要客户端同时进行反馈的需求_,SSE就是不二之选。
对于Websocket来说,_他的优点就是可以同时支持客户端和服务端的双向通讯_。所适用的业务场景:最典型的就是_聊天功能_。这种服务端需要主动向客户端推送信息,并且客户端也有向服务端推送消息的需求时,Websocket就是更好的选择。
建立一个SSE链接 :var source = new EventSource(url);
source.readyState
Content-Type: text/event-stream //文本返回格式
Cache-Control: no-cache //不要缓存
Connection: keep-alive //长链接标识
image.png
SSE:相关文档,文档入口文档入口文档入口文档入口[1]
显然,如果直接看api介绍不论是看这里还是看官网,大部分同学都是比较懵圈的状态,_那么我们写个demo来看一下?_
image.pngdemo请看下方
我更建议您先把Demo跑起来,然后在看看上面这个w3cschool的SSE文档[2]。两个配合一起看,会更方便理解些。
image.png这里Demo前端使用的就是最基本的html静态页面连接,没有使用任何框架。后端选用语言是node,框架是Express。
理论上,把这两段端代码复制过去跑起来就直接可以用了。
npm init //初始化npm
npm i express //下载node express框架
node index //启动服务
image.png
在这一层文件夹下执行命令。
完成以上操作就可以把项目跑起来了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="ul">
</ul>
</body>
<script>
//生成li元素
function createLi(data){
let li = document.createElement("li");
li.innerHTML = String(data.message);
return li;
}
//判断当前浏览器是否支持SSE
let source = ''
if (!!window.EventSource) {
source = new EventSource('http://localhost:8088/sse/');
}else{
throw new Error("当前浏览器不支持SSE")
}
//对于建立链接的监听
source.onopen = function(event) {
console.log(source.readyState);
console.log("长连接打开");
};
//对服务端消息的监听
source.onmessage = function(event) {
console.log(JSON.parse(event.data));
console.log("收到长连接信息");
let li = createLi(JSON.parse(event.data));
document.getElementById("ul").appendChild(li)
};
//对断开链接的监听
source.onerror = function(event) {
console.log(source.readyState);
console.log("长连接中断");
};
</script>
</html>
const express = require('express'); //引用框架
const app = express(); //创建服务
const port = 8088; //项目启动端口
//设置跨域访问
app.all("*", function(req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", '*');
//允许的header类型
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
// 可以带cookies
res.header("Access-Control-Allow-Credentials", true);
if (req.method == 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
})
app.get("/sse",(req,res) => {
res.set({
'Content-Type': 'text/event-stream', //设定数据类型
'Cache-Control': 'no-cache',// 长链接拒绝缓存
'Connection': 'keep-alive' //设置长链接
});
console.log("进入到长连接了")
//持续返回数据
setInterval(() => {
console.log("正在持续返回数据中ing")
const data = {
message: `Current time is ${new Date().toLocaleTimeString()}`
};
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
})
//创建项目
app.listen(port, () => {
console.log(`项目启动成功-http://localhost:${port}`)
})
我是林三心
广州的兄弟可以约饭哦,或者约球~我负责打铁,你负责进球,谢谢~