前端也能这么丝滑!Node + Vue3 实现 SSE 流式文本输出

引言

在现代 Web 应用中,我们经常需要从服务器获取数据并实时显示在页面上。传统的请求-响应模式在处理实时数据更新时效率不高,而 WebSocket 虽然功能强大,但实现相对复杂。今天,我将介绍一种更简单、更轻量级的实时通信技术——Server-Sent Events (SSE),并展示如何使用 Node.js 和 Vue3 实现流式文本输出,让你的前端应用也能丝滑般地展示实时数据!

什么是 SSE?

Server-Sent Events (SSE) 是一种服务器向客户端推送事件的技术。它允许服务器向客户端发送数据流,客户端可以实时接收并处理这些数据。与 WebSocket 相比,SSE 更简单、更轻量级,特别适合服务器到客户端的单向数据传输场景。

SSE 的主要特点:

  • 基于 HTTP 协议,无需额外的端口
  • 自动重连机制
  • 文本消息格式简单(JSON 或纯文本)
  • 单向通信(服务器→客户端)

Node.js 后端实现

首先,我们需要创建一个 Node.js 服务器来发送 SSE 消息。我们将使用 Express 框架来简化开发。

安装依赖

npm init -y
npm install express

创建 SSE 服务器

// server.js
const express = require('express');
const app = express();
const port = 3000;

// 设置 CORS,允许前端访问
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  next();
});

// SSE 端点
app.get('/stream', (req, res) => {
  // 设置 SSE 所需的响应头
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  
  // 模拟发送流式数据
  const sendMessages = () => {
    const messages = [
      "欢迎来到 SSE 演示!",
      "这是一个流式文本输出的例子。",
      "服务器会逐字发送文本内容。",
      "你可以看到文本是如何逐字显示在页面上的。",
      "这就是 SSE 的魅力所在!"
    ];
    
    let messageIndex = 0;
    let charIndex = 0;
    
    const sendNextChar = () => {
      if (messageIndex >= messages.length) {
        // 所有消息发送完毕,关闭连接
        res.write('data: {"done": true}\n\n');
        res.end();
        return;
      }
      
      const currentMessage = messages[messageIndex];
      
      if (charIndex < currentMessage.length) {
        // 发送一个字符
        const char = currentMessage[charIndex];
        res.write(`data: {"char": "${char}", "done": false}\n\n`);
        charIndex++;
        setTimeout(sendNextChar, 50); // 控制发送速度
      } else {
        // 当前消息发送完毕,发送换行符
        res.write('data: {"char": "\\n", "done": false}\n\n');
        messageIndex++;
        charIndex = 0;
        setTimeout(sendNextChar, 200); // 消息间暂停
      }
    };
    
    // 开始发送
    sendNextChar();
  };
  
  // 处理客户端断开连接
  req.on('close', () => {
    console.log('客户端断开连接');
    res.end();
  });
  
  // 开始发送消息
  sendMessages();
});

app.listen(port, () => {
  console.log(`SSE 服务器运行在 http://localhost:${port}`);
});

Vue3 前端实现

现在,让我们创建一个 Vue3 应用来接收 SSE 流并显示文本。

安装依赖

npm create vue@latest
# 或者使用 Vue CLI
# vue create vue-sse-client

实现 SSE 客户端

<!-- App.vue -->
<template>
  <div class="container">
    <h1>SSE 流式文本演示</h1>
    <div class="stream-container">
      <div class="output" ref="output"></div>
      <div class="cursor" :style="{ top: cursorPosition + 'px' }"></div>
    </div>
    <button @click="startStream" :disabled="isStreaming">
      {{ isStreaming ? '接收中...' : '开始接收' }}
    </button>
    <button @click="clearOutput" :disabled="!outputText">清空</button>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const output = ref(null);
const outputText = ref('');
const isStreaming = ref(false);
const eventSource = ref(null);
const cursorPosition = ref(0);

const startStream = () => {
  if (isStreaming.value) return;
  
  isStreaming.value = true;
  outputText.value = '';
  cursorPosition.value = 0;
  
  // 创建 EventSource 连接
  eventSource.value = new EventSource('http://localhost:3000/stream');
  
  eventSource.value.onmessage = (event) => {
    try {
      const data = JSON.parse(event.data);
      
      if (data.done) {
        // 流结束
        isStreaming.value = false;
        eventSource.value.close();
        return;
      }
      
      if (data.char === '\n') {
        // 处理换行
        outputText.value += '\n';
        cursorPosition.value += 20; // 假设每行高度为20px
      } else {
        // 添加字符
        outputText.value += data.char;
        // 更新光标位置(简单估算)
        cursorPosition.value = outputText.value.length * 10; // 假设每个字符宽度为10px
      }
      
      // 滚动到底部
      setTimeout(() => {
        if (output.value) {
          output.value.scrollTop = output.value.scrollHeight;
        }
      }, 0);
    } catch (error) {
      console.error('解析 SSE 数据时出错:', error);
    }
  };
  
  eventSource.value.onerror = (error) => {
    console.error('SSE 错误:', error);
    isStreaming.value = false;
    if (eventSource.value) {
      eventSource.value.close();
    }
  };
};

const clearOutput = () => {
  outputText.value = '';
  cursorPosition.value = 0;
};

// 组件卸载时关闭连接
onUnmounted(() => {
  if (eventSource.value) {
    eventSource.value.close();
  }
});
</script>

<style scoped>
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
  font-family: Arial, sans-serif;
}

h1 {
  color: #333;
  text-align: center;
}

.stream-container {
  position: relative;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 15px;
  height: 300px;
  overflow: hidden;
  margin-bottom: 20px;
}

.output {
  height: 100%;
  overflow-y: auto;
  white-space: pre-wrap;
  word-wrap: break-word;
  line-height: 1.5;
}

.cursor {
  position: absolute;
  left: 5px;
  width: 2px;
  height: 20px;
  background-color: #42b983;
  animation: blink 1s infinite;
}

@keyframes blink {
  0% { opacity: 1; }
  50% { opacity: 0; }
  100% { opacity: 1; }
}

button {
  padding: 8px 16px;
  margin-right: 10px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

button:hover {
  background-color: #3aa876;
}

button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}
</style>

完整示例

将上述代码整合起来,我们就有了一个完整的 Node.js + Vue3 SSE 流式文本输出示例。

  1. 启动 Node.js 服务器:
node server.js
  1. 启动 Vue 应用:
npm run dev
  1. 在浏览器中访问 Vue 应用,点击"开始接收"按钮,你将看到文本逐字显示的效果!

实际应用场景

SSE 流式文本输出在许多场景中非常有用:

  1. 聊天应用:实时显示消息
  2. 代码编辑器:显示代码执行过程
  3. 文档协作:多人同时编辑文档
  4. 实时日志:显示服务器日志输出
  5. AI 对话:显示 AI 生成的回答过程
  6. 长任务进度:显示长时间运行任务的进度

总结

通过本文,我们学习了如何使用 Node.js 和 Vue3 实现 SSE 流式文本输出。SSE 是一种简单而强大的技术,特别适合服务器向客户端推送实时数据的场景。相比 WebSocket,SSE 更轻量级,实现更简单,适合大多数单向通信需求。

关键点回顾:

  • SSE 基于 HTTP 协议,适合服务器到客户端的单向通信
  • Node.js 后端需要设置特定的响应头来启用 SSE
  • 前端使用 EventSource API 接收 SSE 流
  • 可以通过控制发送速度来实现流式效果

希望这篇文章能帮助你理解 SSE 并在你的项目中应用它!如果你有任何问题或建议,欢迎在评论区留言。觉得有用就点个关注!我会持续分享更多实用工具和效率技巧。想直接体验?各类免费、开箱即用的在线工具,都在我的【星点工具箱】网站等你来玩!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐