核心要点
Content-type
头部(值为 text/event-stream
)和 UTF-8 字符编码来提供内容。服务器发送事件的语法包括数据、事件类型、事件标识符和重试间隔。onmessage
函数(仅用于发送消息事件的应用程序),但 addEventListener
方法对于处理自定义事件更灵活。假设您国家的国家篮球队正在参加世界篮球锦标赛。您想关注比赛,但由于比赛时间与您的工作时间冲突,无法观看。
幸运的是,您的国家新闻服务拥有一个非常出色的 Web 开发团队。他们构建了一个体育信息滚动显示器,每当出现犯规或得分时都会更新。您访问一个 URL,更新就会直接推送到您的浏览器。当然,您会想知道他们是如何做到的。答案是:服务器发送事件。
服务器发送事件是一种使用流将数据和/或 DOM 事件从服务器推送到客户端的方法。它适用于股票行情、体育比分、航班追踪、电子邮件通知——任何需要定期更新数据的情况。
等等!
我听到您说,我们不能已经使用 XMLHttpRequest 或 WebSockets 来做到这一点吗?
是的,可以。但是,这样做需要扩展这些对象来实现 EventSource 本身的功能。
由于服务器发送事件是数据流,因此它们需要长期连接。您需要使用能够处理大量同时连接的服务器。当然,事件驱动服务器特别适合流式传输事件。这些包括 Node.js、Juggernaut 和 Twisted。对于 Nginx,可以使用 nginx-push-stream-module 模块。但是,服务器配置不在本文讨论范围之内,并且会因您使用的服务器而异。
让我们看看如何使用 EventSource 对象订阅流。然后,我们将看看如何发送和处理事件。
创建 EventSource 对象很简单。
var evtsrc = new EventSource('./url_of/event_stream/',{withCredentials:false});
EventSource 构造函数最多接受两个参数:
withCredentials
属性的值。字典在语法上类似于对象,但它们实际上是具有定义的名称-值对的关联数据数组。在本例中,withCredentials
是唯一可能的字典成员。其值可以是 true 或 false。(要了解有关字典的更多信息,请参阅 Web IDL 规范。)
仅当需要用户凭据(cookie)的跨域请求时,才需要包含字典参数。到目前为止,没有浏览器支持跨域 EventSource 请求。因此,我们不会在示例中包含第二个参数。
当 EventSource 连接打开时,它将触发一个 open
事件。我们可以通过设置 onopen
属性来定义一个处理该事件的函数。
var evtsrc = new EventSource('./url_of/event_stream/'); evtsrc.onopen = function(openevent){ // 连接打开时执行的操作 }
如果我们的连接出现问题,将触发一个错误。我们可以使用 onerror
属性为这些事件定义一个处理程序函数。我们将在“处理错误”部分讨论一些错误事件的原因。
evtsrc.onerror = function(openevent){ // 发生错误时执行的操作 }
流式事件默认为消息事件。要处理消息事件,我们可以使用 onmessage
属性来定义一个处理程序函数。
evtsrc.onmessage = function(openevent){ // 接收到消息事件时执行的操作 }
我们还可以使用 addEventListener()
来监听事件。这是处理自定义事件的唯一方法,正如我们将在“处理事件”部分看到的那样。
var onerrorhandler = function(openevent){ // 执行的操作 } evtsrc.addEventListener('error',onerrorhandler,false);
要关闭连接,请使用 close()
方法。
evtsrc.close();
因此,我们创建了 EventSource 对象,并为打开、消息和错误事件定义了处理程序。但是,为了使此方法有效,我们需要一个流式传输事件的 URL。
服务器发送事件是作为来自 URL 的流的一部分交付的文本片段。为了让浏览器将我们的数据视为流,我们必须:
Content-type
头部(值为 text/event-stream
)来提供内容;服务器发送事件的语法很简单。它由一个或多个冒号分隔的字段名称-值对组成,后跟一个换行符。字段名称可以包含四个可能的值之一。
data:
:要发送的信息。event:
:正在分派的事件类型。id:
:客户端重新连接时要使用的事件标识符。retry:
:浏览器尝试重新连接到 URL 之前应经过多少毫秒。其中,只有 data
字段是必需的。
在此示例中,我们将发送一个事件来宣布我们的锦标赛比赛中哪些球队正在比赛。当浏览器接收到此文本时,它将分派一个消息事件。
var evtsrc = new EventSource('./url_of/event_stream/',{withCredentials:false});
data
字段的值将成为消息事件的 data
属性的值。如上所述,服务器发送事件默认为消息事件。但正如我们稍后将讨论的那样,我们还可以通过包含 event
字段来分派自定义事件。
我们还可以将几条数据作为单个事件发送。每块数据后面都应该跟着一个换行符(换行符、回车符或两者)。在这里,我们正在追加一个包含此游戏位置和出席人数的事件。
var evtsrc = new EventSource('./url_of/event_stream/'); evtsrc.onopen = function(openevent){ // 连接打开时执行的操作 }
对于此事件,data
属性的值将是:Air Canada CentrenToronto, Ontario, CanadanAttendance: 19,800。
请注意事件之间的空行。为了让客户端接收事件,事件后面必须跟一个空行。注释以冒号开头。
除非我们另行指定,否则事件的类型为消息。为此,我们需要包含一个 event
字段。在下面的示例中,我们将向我们的流中添加两个 startingfive
事件,并将我们的数据作为 JSON 格式的字符串发送。
evtsrc.onerror = function(openevent){ // 发生错误时执行的操作 }
在这里,我们需要监听 startingfive
事件而不是消息事件。但是,我们的 data
字段仍然会成为事件的 data
属性的值。
我们将在“处理事件”部分讨论 data
属性和 MessageEvent 接口。
现在,虽然服务器确实将事件推送到浏览器,但现实情况要细微一些。如果服务器保持连接打开,EventSource 请求将是一个扩展的请求。如果它关闭,浏览器将等待几秒钟,然后重新连接。例如,如果 URL 发送一个文件结束标记,则连接可能会关闭。
每个浏览器都设置了自己的默认重新连接间隔。大多数会在 3 到 6 秒后重新连接。但是,您可以通过包含 retry
字段来控制此间隔。retry
字段指示客户端在重新连接到 URL 之前应等待多少毫秒。让我们从上面的示例构建并更改我们的事件以包含 5 秒(5000 毫秒)的重试间隔。
evtsrc.onmessage = function(openevent){ // 接收到消息事件时执行的操作 }
事件流可以保持活动状态,只要客户端已连接即可。根据您的架构和应用程序,您可能希望服务器定期关闭连接。
当浏览器重新连接到 URL 时,它将接收连接点处可用的任何数据。但是,对于游戏信息滚动显示器,我们可能希望让访问者了解他或她错过了什么。这就是为什么将 id 设置为每个事件的最佳实践。在下面的示例中,我们正在发送 id 作为得分事件的一部分。
var evtsrc = new EventSource('./url_of/event_stream/',{withCredentials:false});
其值应对于流是唯一的。在本例中,我们使用的是投篮得分的时间。
id
字段成为此事件对象的 lastEventId
属性。但它还有另一个用途。如果连接关闭,浏览器将在其下一个请求中包含一个 Last-Event-ID
头部。将其视为流的书签。如果存在 Last-Event-ID
头部,您可以调整应用程序的响应,以便仅发送在其之后的事件。
如上所述,所有事件默认为消息事件。每个消息事件都有三个属性,由 MessageEvent 接口定义。
onmessage
函数都将被调用。这对于您只发送消息事件的应用程序来说效果很好。但是,如果您想发送得分或 startingfive
事件(如我们的示例中所示),其局限性就会变得显而易见。使用 addEventListener
更灵活。在下面的代码中,我们正在使用 addEventListener
处理 startingfive
事件。
var evtsrc = new EventSource('./url_of/event_stream/'); evtsrc.onopen = function(openevent){ // 连接打开时执行的操作 }
(后续部分,由于篇幅限制,请分段提问。)
以上是服务器范围事件的详细内容。更多信息请关注PHP中文网其他相关文章!