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 可以监听三种类型的事件:messageopenerror

  • 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 仅用于服务器到客户端的单向通信。如果你需要从客户端向服务器发送数据,建议使用 fetchXMLHttpRequest 结合 EventSource,或者采用 WebSocket 进行双向通信。

相关推荐
C语言魔术师10 分钟前
【小游戏篇】三子棋游戏
前端·算法·游戏
匹马夕阳1 小时前
Vue 3中导航守卫(Navigation Guard)结合Axios实现token认证机制
前端·javascript·vue.js
你熬夜了吗?1 小时前
日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件
前端·vue.js·信息可视化
桂月二二7 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062069 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb9 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角9 小时前
CSS 颜色
前端·css
九酒9 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔10 小时前
HTML5 新表单属性详解
前端·html·html5
lee57610 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm