最近的开发任务有一项是需要从服务端推送消息到浏览器端,第一时间想到的是websocket和ajax轮询,但是我只需要从服务端推送到客户端,用websocket感觉有点浪费,ajax轮询又不符合要求;于是查了查文档,才发现有更轻量级的解决方案:EventSource。
顾名思义,EventSource可以翻译为事件源,它本质是一个网络事件接口,会在浏览器和服务器之间建立一个持久化的连接,数据以单向的形式从服务端以text/event-stream 格式发送到客户端。想要深入了解请参照mdn文档。
建立EventSource很简单,只需如下几个步骤
-
服务端准备接口,接口有如下要求:
- 接口的响应头中的Content-Type 需要设置为:text/event-stream;
- 服务端准备一个可写流;
- 服务端返回这个流
- 服务端往流中写入数据时需要遵循以下格式:
event: customEvent\n'+"data:"+data+"" + "\n\n
实现代码如下:
const { Readable, PassThrough } = require("stream"); const Koa = require("koa"); const Router = require("koa-router"); const { start } = require("repl"); const { promises } = require("fs") const app = new Koa(); const router = new Router(); router.get("/se", (ctx) => { const ss = new PassThrough(); ctx.set({ "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", "Access-Control-Allow-Origin":"*", "Access-Control-Allow-Headers":"Content-Type" }); ctx.body = ss; // 使用定时器来模拟任务 setInterval(()=>{ const date = { date: `当前时间:北京时间${new Date().toLocaleTimeString()}` }; // 此处的customEventName可以按照自己的需求定义 const data = 'event: customEventName\n'+"data:"+JSON.stringify(date)+"" + "\n\n"; ss.push(data); }, 1000) }); app.use(router.routes()); app.listen(8080, ()=>{ console.log('server is running') })
-
前端实例化EventSource对象,并监听同名事件,实现代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id='container'></div> <script> // 实例化eventSource const es = new EventSource('http://127.0.0.1:8080/se'); // 监听error事件,可在此处重新订阅 es.addEventListener('error', (e)=>{ console.error(e); }) // 监听事件并处理 es.addEventListener('customEventName', (e)=>{ container.innerHTML = e.data; }) </script> </body> </html>
至此,实现了一个简单的单向消息推送;