前端开发者们在设计基于 HTTP 协议的应用时,会经常遇到浏览器缓存问题。为了提高应用的性能,浏览器通常会缓存一些静态文件(如 JS、CSS 文件等),从而避免在后续访问中重复下载。然而,当我们使用 Server-sent-events 技术时,HTTP 缓存就会带来一些问题。
Server-sent-events
Server-sent-events(SSE),也称为 EventSource,是一种向客户端推送事件的技术。通过 SSE,服务器可以实时地向客户端发送事件和数据,而客户端则可以通过 JavaScript 进行监听和处理。SSE 基于 HTTP,使用的是长连接,因此可以实现实时的双向通信。
下面是一个 SSE 的基本示例:
-- -------------------- ---- ------- ----- ----------- - --- -------------------- --------------------------------------- --------------- - -------------------- ----------- ------------ --- ------------------------------------- --------------- - ----------------------- ------- ---
在这个示例中,我们通过 EventSource
对象向 /sse
路径建立 SSE 连接。当服务器发送消息时,message
事件会被触发,此时我们可以通过 event.data
获取到服务器发送的数据。如果出现错误,error
事件会被触发。
HTTP 缓存
HTTP 缓存是指浏览器缓存一些静态资源,避免重复下载。HTTP 缓存的实现方式有两种:基于时间的缓存和基于内容的缓存。
基于时间的缓存是指浏览器在第一次请求某个资源时,会将Cache-Control
和 Expires
等头部信息保存下来,然后在后续访问时,仅当缓存过期或者不存在时才会重新下载。基于内容的缓存是指浏览器比较请求头部信息和响应头部信息中的 ETag
或 Last-Modified
等信息,如果一致则表示资源未发生变化,可以直接从缓存中读取。
Server-sent-events 和 HTTP 缓存
SSE 本身并不会影响 HTTP 缓存,但是当使用了 HTTP 缓存时,SSE 发送的事件会被缓存,从而导致客户端无法实时接收到服务器的推送。
为了更好地理解这个问题,我们可以假设有一个 SSE 服务器不停地发送数字,客户端进行监听和显示。下面是使用基于时间的缓存的示例:
-- -------------------- ---- ------- --------- ----- ----- ---------- ------ ----- ---------------- ---------- ------------ -------- -------- ------------------- - ----- ----------- - --- -------------------- --------------------------------------- --------------- - ------------------------------------------- - ----------- --- ------------------------------------- --------------- - ----------------------- ------- --- - --------- ------- ----- ----------------------------- ------- --------- ---- ---------------- ------- -------
在这个示例中,我们使用 onload
事件调用 createEventSource()
函数建立 SSE 连接,当服务器发送消息时,我们使用 JavaScript 更新页面中的 data
元素。
现在我们来看一下 SSE 服务器代码:
let counter = 0; setInterval(function() { counter += 1; console.log('Counter:', counter); SSE.send(counter); }, 1000);
这个服务器会不断地向客户端发送数字,每秒钟发一次。
假设我们现在在浏览器中打开这个页面,并启用基于时间的缓存。这时,我们在第一次打开页面时会发现数字正常地被更新。然而,如果你打开 DevTools,查看 Network 面板,你会发现 /sse
路径并没有被重新请求,也就是说,SSE 事件被缓存了。
现在,我们关闭浏览器,1 分钟后再打开页面,你会发现页面上的数字依然是上一次的数字。这是因为,虽然连接是实时的,但是由于使用了基于时间的缓存,浏览器仍然会从缓存中读取过期的数据。
解决方案
为了解决这个问题,我们需要做以下两点改进:
- 禁用 SSE 事件的缓存。
- 在 SSE 事件中添加一个唯一的标识符,来避免客户端接受到的事件被缓存。
为了禁用 SSE 事件的缓存,我们可以在服务器的响应头部信息中添加 Cache-Control: no-cache
和 Pragma: no-cache
等信息。这样浏览器就不会缓存这些事件了。在 Express 框架中,这个可以通过以下代码实现:
res.set({ 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive', 'X-Accel-Buffering': 'no' });
为了在 SSE 事件中添加一个唯一的标识符,我们可以使用 Date.now()
方法来生成一个随机数,然后把这个随机数放在 SSE 事件的数据中。这个随机数可以作为事件的标识符使用,避免客户端接受到的事件被缓存。
下面是改进后的 SSE 服务器代码:
-- -------------------- ---- ------- --- ------- - -- ---------------------- - ------- -- -- ----------------------- --------- ---------- ----- ------ ----------------- -------------- --- -- ------
在这个示例中,我们在 SSE 事件的数据中添加了一个随机数作为标识符。这样,浏览器就无法把这些事件缓存下来了。
结论
在使用 SSE 技术时,HTTP 缓存会给我们带来一些问题。通过禁用 SSE 事件的缓存,我们可以解决这个问题,并且通过添加唯一的标识符,我们可以避免客户端接受到的事件被缓存。在实际项目中,我们需要根据具体的应用场景和需求,决定是否使用 HTTP 缓存以及如何优化 SSE 技术的使用。
参考链接
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/671f4d452e7021665efce431