EventSource

什么是EventSource

EventSource 是一个用于服务器推送事件(Server-Sent Events, SSE)的接口,它允许服务器推送实时更新到浏览器。与 WebSocket 不同,SSE 是单向的(服务器到客户端),适用于更新频率不高的实时通知、消息推送等场景。下面是关于 EventSource 的详细介绍,包括使用示例和注意事项。

基本概念

  • 服务器推送事件(SSE):服务器向客户端推送实时更新,而不需要客户端发起请求。
  • EventSource 接口:浏览器端用于接收服务器推送事件的 API。

创建 EventSource 实例

要创建一个 EventSource 实例,需要传入一个 URL,这个 URL 指向服务器端的事件流端点:

javascript 复制代码
const eventSource = new EventSource('https://example.com/events');

监听事件

EventSource 可以监听三种类型的事件:message、open 和 error。

  • message 事件:当服务器发送一个消息时触发。
  • open 事件:当连接被打开时触发。
  • error 事件:当发生错误时触发。
javascript 复制代码
eventSource.onmessage = function(event) {
  console.log('Message:', event.data);
};

eventSource.onopen = function() {
  console.log('Connection opened.');
};

eventSource.onerror = function(event) {
  if (event.readyState === EventSource.CLOSED) {
    console.log('Connection closed.');
  } else {
    console.log('Error:', event);
  }
};

自定义事件

除了默认的 message 事件,你还可以监听自定义事件。服务器可以通过 event 字段来定义事件类型,客户端使用 addEventListener 来监听。

服务器发送(示例):

复制代码
event: customEvent
data: This is a custom event

客户端监听:

javascript 复制代码
eventSource.addEventListener('customEvent', function(event) {
  console.log('Custom Event:', event.data);
});

服务器端设置

服务器端需要设置适当的响应头来支持 SSE:

http 复制代码
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

并按照指定的格式发送数据:

http 复制代码
data: This is a message\n\n

例如,使用 Node.js 和 Express 的实现:

javascript 复制代码
const express = require('express');
const app = express();

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  setInterval(() => {
    res.write(`data: ${JSON.stringify({ message: 'Hello, World!' })}\n\n`);
  }, 1000);
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

关闭连接

当不再需要接收事件时,可以关闭 EventSource 连接:

javascript 复制代码
eventSource.close();
console.log('Connection closed.');

完整示例

以下是一个完整的前端和后端示例,展示了如何使用 EventSource 和 SSE。

前端(HTML + JavaScript):

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>EventSource Example</title>
</head>
<body>
  <h1>Server-Sent Events Example</h1>
  <div id="messages"></div>
  <script>
    const eventSource = new EventSource('http://localhost:3000/events');

    eventSource.onmessage = function(event) {
      const messageElement = document.createElement('div');
      messageElement.textContent = 'Message: ' + event

```html
      messageElement.textContent = 'Message: ' + event.data;
      document.getElementById('messages').appendChild(messageElement);
    };

    eventSource.onopen = function() {
      console.log('Connection opened.');
    };

    eventSource.onerror = function(event) {
      if (event.readyState === EventSource.CLOSED) {
        console.log('Connection closed.');
      } else {
        console.log('Error:', event);
      }
    };

    // Example of listening to a custom event
    eventSource.addEventListener('customEvent', function(event) {
      const customEventElement = document.createElement('div');
      customEventElement.textContent = 'Custom Event: ' + event.data;
      document.getElementById('messages').appendChild(customEventElement);
    });

    // Close the EventSource after 10 seconds for demonstration purposes
    setTimeout(() => {
      eventSource.close();
      console.log('Connection closed.');
    }, 10000);
  </script>
</body>
</html>

后端(Node.js + Express):

javascript 复制代码
const express = require('express');
const app = express();

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  // Send a simple message every second
  const intervalId = setInterval(() => {
    res.write(`data: ${JSON.stringify({ message: 'Hello, World!' })}\n\n`);
  }, 1000);

  // Send a custom event every 5 seconds
  const customEventIntervalId = setInterval(() => {
    res.write(`event: customEvent\ndata: This is a custom event\n\n`);
  }, 5000);

  // Clear intervals when client closes connection
  req.on('close', () => {
    clearInterval(intervalId);
    clearInterval(customEventIntervalId);
  });
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

详细解释

  1. 前端代码解释:

    • EventSource 实例 :const eventSource = new EventSource('http://localhost:3000/events'); 创建一个新的 EventSource 实例,连接到服务器的 /events 端点。
    • 事件监听 :
      • onmessage 处理默认的 message 事件,显示从服务器接收到的消息。
      • onopen 处理连接打开事件,输出连接已打开的信息。
      • onerror 处理错误事件,输出错误信息或连接关闭信息。
      • addEventListener('customEvent', ...) 处理自定义事件 customEvent,显示从服务器接收到的自定义事件数据。
    • 关闭连接 :使用 setTimeout 在 10 秒后关闭 EventSource 连接。
  2. 后端代码解释:

    • Express 服务器 :创建一个简单的 Express 服务器,监听 /events 路由。
    • 设置响应头 :确保设置 Content-Type, Cache-Control, 和 Connection 头以支持 SSE。
    • 发送数据 :
      • 使用 setInterval 每秒发送一个消息。
      • 使用另一个 setInterval 每 5 秒发送一个自定义事件。
    • 清理:当客户端关闭连接时,清理定时器以避免内存泄漏。

注意事项

  • 浏览器支持 :EventSource 在现代浏览器中得到广泛支持,但在 IE 和旧版浏览器中不支持。你可能需要考虑替代方案或 Polyfill。
  • 单向通信:SSE 是单向通信,从服务器到客户端。如果需要双向通信,应该考虑使用 WebSocket。
  • 重连机制 :EventSource 内置了自动重连机制,连接断开时会自动尝试重新连接。
  • 带宽考虑:SSE 使用 HTTP/1.1 长连接,适合较低频率的消息推送。如果消息频率较高,应考虑 WebSocket。

通过上述示例,你应该能够理解和使用 EventSource 来实现服务器推送事件,从而在你的应用中实现实时更新。


可以使用eventSource向后端传递数据吗

EventSource 是一种用于服务器推送事件(Server-Sent Events, SSE)的技术,主要用于服务器向客户端推送数据。这是一种单向通信,从服务器到客户端。它不支持从客户端向服务器发送数据。

如果你需要双向通信(即从客户端向服务器发送数据并接收服务器的响应),有几种常见的替代方案可以考虑:

替代方案

  1. XHR (XMLHttpRequest) / Fetch API:

    • 用于在需要的时候从客户端向服务器发送请求。
    • 可以与 EventSource 配合使用,处理从服务器接收推送消息。
  2. WebSocket:

    • 支持全双工通信,允许客户端和服务器相互发送数据。
    • 适用于实时性要求较高的应用场景。

使用 Fetch API 发送数据

假设你正在使用 EventSource 接收服务器推送的数据,同时需要发送数据到服务器,你可以使用 fetch API 来实现。

前端示例:

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>EventSource with Fetch</title>
</head>
<body>
  <h1>EventSource with Fetch Example</h1>
  <div id="messages"></div>
  <button id="sendDataButton">Send Data</button>

  <script>
    const eventSource = new EventSource('http://localhost:3000/events');

    eventSource.onmessage = function(event) {
      const messageElement = document.createElement('div');
      messageElement.textContent = 'Message: ' + event.data;
      document.getElementById('messages').appendChild(messageElement);
    };

    eventSource.onopen = function() {
      console.log('Connection opened.');
    };

    eventSource.onerror = function(event) {
      if (event.readyState === EventSource.CLOSED) {
        console.log('Connection closed.');
      } else {
        console.log('Error:', event);
      }
    };

    document.getElementById('sendDataButton').addEventListener('click', () => {
      fetch('http://localhost:3000/send-data', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ message: 'Hello, Server!' })
      })
      .then(response => response.json())
      .then(data => {
        console.log('Success:', data);
      })
      .catch((error) => {
        console.error('Error:', error);
      });
    });
  </script>
</body>
</html>

后端示例(Node.js + Express):

javascript 复制代码
const express = require('express');
const app = express();

app.use(express.json());

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  const intervalId = setInterval(() => {
    res.write(`data: ${JSON.stringify({ message: 'Hello, Client!' })}\n\n`);
  }, 1000);

  req.on('close', () => {
    clearInterval(intervalId);
  });
});

app.post('/send-data', (req, res) => {
  const clientMessage = req.body.message;
  console.log('Received from client:', clientMessage);
  res.json({ status: 'success', receivedMessage: clientMessage });
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

使用 WebSocket 实现双向通信

如果你需要更复杂的双向通信,可以考虑使用 WebSocket:

前端示例:

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>WebSocket Example</title>
</head>
<body>
  <h1>WebSocket Example</h1>
  <div id="messages"></div>
  <button id="sendDataButton">Send Data</button>

  <script>
    const socket = new WebSocket('ws://localhost:3000');

    socket.onopen = function() {
      console.log('WebSocket connection opened.');
    };

    socket.onmessage = function(event) {
      const messageElement = document.createElement('div');
      messageElement.textContent = 'Message: ' + event.data;
      document.getElementById('messages').appendChild(messageElement);
    };

    socket.onerror = function(event) {
      console.error('WebSocket error:', event);
    };

    socket.onclose = function() {
      console.log('WebSocket connection closed.');
    };

    document.getElementById('sendDataButton').addEventListener('click', () => {
      const message = 'Hello, Server!';
      socket.send(message);
      console.log('Sent:', message);
    });
  </script>
</body>
</html>

后端示例(Node.js + ws 模块):

javascript 复制代码
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 3000 });

server.on('connection', ws => {
  console.log('New client connected.');

  ws.on('message', message => {
    console.log('Received from client:', message);
    ws.send(`Hello, Client! Received your message: ${message}`);
  });

  ws.on('close', () => {
    console.log('Client disconnected.');
  });

  ws.send('Welcome, Client!');
});

总结

EventSource 仅用于服务器到客户端的单向通信。如果你需要从客户端向服务器发送数据,建议使用 fetch 或 XMLHttpRequest 结合 EventSource,或者采用 WebSocket 进行双向通信。

相关推荐
deli00715 分钟前
32K 上下文塞满后,AI 最先忘掉什么?120 轮对话实测
前端
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户479492835691510 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng12 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding13 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js