前言
前面我们已经聊了ajax,它的特点是浏览器必须先发起请求,服务器才能给出对应的响应,想一想能不能让服务器主动向浏览器推送数据呢?那么这篇文章我们来聊一聊服务器推送功能。
轮询
假设你现在需要去做一个球赛直播页面,一个主播在后台文字直播比赛,那么这就要求解说数据尽可能的实时到达浏览器,那么我们如何解决呢?
最容易想到的就是用ajax轮询,写个定时器,每隔几秒钟去后端请求一次数据,这当然是可以的,但是这种方式并不是很优雅。因为浏览器每次需要主动去询问服务器有没有数据,如果反过来服务器有数据能主动告之浏览器岂不是更好?
初识推送
那么如何能让服务器自己主动推送数据呢?下面我们先实现一下服务端的代码,如下:
如图1,我们用express起了一个小服务,每当服务器接收到请求在响应时就会把Content-type设置为text/event-stream,这是实现服务器推送的关键,它表示这次链接采用流实现并且整个页面生命周期都保持这一个链接的打开状态!
页面上的实现和普通ajax类似,但也有点不同,如下
如图2,获取数据时的状态是在3,因为此链接一直处于打开状态。
当你同时运行服务端和客户端的代码时,你会发现浏览器控制台会一直打印123,此刻我们就已经实现了服务器推送的功能。
SSE
根据上面介绍的服务器推送的特点,浏览器自身也实现了这样的接口——SSE。
我们先看一下浏览器端如何使用,如下:
如图3,使用EventSource创建一个实例对象,参数是流实现的接口,下面绑定了几个事件,
- open,当连接上之后就会立即触发;
- message,服务器向客户端发送数据的默认事件,通过e.data可以获取到数据;
- foo,自定义事件(SSE支持自定义事件);
- error,当链接发生错误时触发。
下面我们再看一下服务端实现,如下:
如图4所示,和图1中的代码雷同,先起一下服务端,再打开页面,看一下现象:
从现象看我们已经实现了服务器推送,符合预期,很好!但是要注意图4中响应数据时的写法,以data: 开头会默认触发页面中message事件,以 结尾结束一次推送,如果一行写不下可以用 分割;
下面我们看看如何触发自定义事件,代码如下:
如图6,我们加一行字符串——'event:' + 事件名 + ' ',这样就会触发页面中的foo事件而不是message事件,现象如下:
据说SSE具有断线重连能力,我们试一下,看看是真是假?
图8证明传言不虚,当把服务关掉,页面在不停的尝试重连,当服务再次启动,页面就会立马链接上。如果服务端在发送数据时加一行字符串——"retry: " + 时间 + " " ,可以设置断开后重新再链接的间隔时间,这个就不演示了。
到目前为止,推送还是无状态的,如果链接断开了,再次重连,服务器是不知道先前已经推送了什么数据,为了解决这个问题,在每次推送时可以加一行字符串——"id: " + 序号 + " ",如下:
我们再来看一下现象,如下:
这个序号在页面上用事件对象的lastEventId属性可以拿到,当下一次重新连接时,浏览器会把最后一次序号放在请求头的Last-Event-ID字段中,所以服务端可以通过这个请求头属性获取之前数据推送的情况。
总结
这篇文章主要讲解SSE如何使用,它是浏览器自带的API,如果浏览器不兼容,我们也可以对它做兼容。从以上的介绍可以看出它是单向的,连接后只能是服务器向浏览器推送,所以它非常适合仅有查询的需求,像球赛直播、股票类的需求。