靓仔来看 WebSocket 实战教程(Node.js + React)

529 阅读4分钟

我是web socket 彭于晏,我喂自己袋盐,请和我双工通信,亲密连接。

随着实时通信需求的增加,WebSocket 在前后端开发中的地位日益提升。本文将基于 Node.js + Express + React + Ant Design 实现一个完整的 WebSocket 聊天室 Demo,包含原生实现与优化建议,适合入门者快速掌握 WebSocket 的实际应用。

0.png


🧪 使用场景分析

WebSocket 是一种持久连接协议,允许服务端主动推送消息给客户端,适用于以下场景:

  • 实时聊天 / 通知系统
  • 实时协作编辑(如多人文档)
  • 实时股票、币价、物流追踪
  • 游戏对战通信
  • 实时问答直播弹幕系统

🧠 一句话总结:

如果你有高频率数据更新 + 服务端推送需求,WebSocket 是更优解。


📦 技术栈简介

  • 前端:React + TypeScript + Ant Design
  • 后端:Node.js + Express + ws(WebSocket 原生库)
  • 通信协议:WebSocket 标准协议(可升级为 socket.io)

🖥️ 后端实现(Node.js + ws)

安装依赖:

npm install ws

后端代码结构:

expressDemo
├── app.js          // Express 主入口
├── routes/
│   └── ws/index.js // WebSocket 服务初始化

1️⃣ WebSocket 处理逻辑(ws/index.js)

// ws/index.js
import { WebSocketServer } from "ws";

const clients = new Set();

export function initWebSocket(server) {
  const wss = new WebSocketServer({ server });

  wss.on("connection", (ws) => {
    console.log("客户端连接 WebSocket");
    clients.add(ws);

    ws.on("message", (message) => {
      console.log("收到消息:", message.toString());

      // 广播消息
      clients.forEach((client) => {
        if (client.readyState === ws.OPEN) {
          client.send(message.toString());
          client.send(`你好,${message.toString()}`);
        }
      });
    });

    ws.on("close", () => {
      console.log("客户端断开");
      clients.delete(ws);
    });
  });

  console.log("WebSocket 服务已启动");
}

2️⃣ 启动 Express + WebSocket(app.js)

import express from "express";
import http from "http";
import bodyParser from "body-parser";
import cors from "cors";
import { initWebSocket } from "./routes/ws/index.js";

const app = express();
const server = http.createServer(app);

app.use(cors());
app.use(bodyParser.json());
app.use(express.static("public"));

// 启用 WebSocket
initWebSocket(server);

server.listen(8088, () => {
  console.log(`服务器启动成功:http://localhost:8088`);
});

💻 前端实现(React + Ant Design)

WebSocket 聊天室组件(WS.tsx

tsx
复制编辑
import {
  message as AntdMessage,
  Button,
  Card,
  Input,
  List,
  Typography,
} from "antd";
import { useEffect, useRef, useState } from "react";

const { Text } = Typography;
const WS_URL = "ws://localhost:8088/ws";

export default function WS() {
  const [msgList, setMsgList] = useState<string[]>([]);
  const [inputMsg, setInputMsg] = useState("");
  const wsRef = useRef<WebSocket | null>(null);

  useEffect(() => {
    const ws = new WebSocket(WS_URL);
    wsRef.current = ws;

    ws.onopen = () => AntdMessage.success("已连接 WebSocket");
    ws.onmessage = (event) => {
      setMsgList((prev) => [...prev, event.data]);
    };
    ws.onclose = () => AntdMessage.error("WebSocket 已断开");

    return () => {
      ws.close();
    };
  }, []);

  const sendMessage = () => {
    if (!inputMsg.trim()) return AntdMessage.warning("请输入消息内容");
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(inputMsg);
      setInputMsg("");
    } else {
      AntdMessage.error("连接已断开,无法发送");
    }
  };

  return (
    <div style={{ padding: 48, maxWidth: 600, margin: "0 auto" }}>
      <Card title="WebSocket 聊天室 Demo" bordered>
        <List
          size="small"
          dataSource={msgList}
          renderItem={(item, index) => (
            <List.Item key={index}>
              <Text>{item}</Text>
            </List.Item>
          )}
          style={{ maxHeight: 300, overflowY: "auto", marginBottom: 16 }}
        />
        <Input.Group compact>
          <Input
            style={{ width: "calc(100% - 90px)" }}
            value={inputMsg}
            placeholder="请输入消息..."
            onChange={(e) => setInputMsg(e.target.value)}
            onPressEnter={sendMessage}
          />
          <Button type="primary" onClick={sendMessage}>
            发送
          </Button>
        </Input.Group>
      </Card>
    </div>
  );
}

关键代码解释

  • 1.创建一个 WebSocket 连接
const socket = new WebSocket("ws://localhost:8080");
  • 2.onopen 当WebSocket 的连接状态readyState 变为1时调用;这意味着当前连接已经准备好发送和接受数据。
socket.onopen = function(event) {
  console.log("WebSocket is open now.");
};

  • 3.onmessage message 事件会在 WebSocket 接收到新消息时被触发
// 监听消息
socket.addEventListener("message", function (event) {
  console.log("Message from server ", event.data);
});
  • 4.close WebSocket.onclose 属性返回一个事件监听器,这个事件监听器将在 WebSocket 连接的readyState 变为 CLOSED时被调用,它接收一个名字为“close”的 CloseEvent 事件。
WebSocket.onclose = function(event) {
  console.log("WebSocket is closed now.");
};
  • 5.send WebSocket.send()  方法将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount的值。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。
WebSocket.send("Hello server!");
const sendMessage = () => {
    if (!inputMsg.trim()) {
      AntdMessage.warning("请输入消息内容");
      return;
    }
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(inputMsg);
      setInputMsg("");
    } else {
      AntdMessage.error("连接已断开,无法发送");
    }
  };

更多详细介绍MDN链接 developer.mozilla.org/zh-CN/docs/…

image.png

image.png

💡 小贴士:在浏览器开发者模式中,在NetWork 得 Socket中,可以实时查看两者客户端与服务端得消息传输内容。在卸载页面时 useEffect(() => { const ws = new WebSocket(WS_URL); wsRef.current = ws; return () => { ws.close(); }; }, []);return加上卸载函数。


⚙️ 进阶优化建议

虽然 ws 已可实现基本通信,但在实际生产中推荐使用更高级封装,如:

✅ 推荐使用 socket.io 的原因:

优点说明
自动重连网络波动后可自动重连
自定义事件支持 .emit('xxx') / .on('xxx') 事件系统
内建心跳机制自动处理断线和心跳,连接更稳定
兼容性好可兼容旧浏览器和 HTTP 长轮询兜底
社区强大文档丰富、案例多、生态完善

可选替代库:

💡 小贴士:靓仔,具体使用哪个第三方库看你使用场景,我这边上述展示代码,前端代码没有将ws得代码封装到utils,规范化的话请封装到utils使用。


🔚 总结

关键点描述
是否适合使用 WebSocket如果是高实时性需求(聊天、直播、推送),非常适合
生产环境推荐封装库如 socket.io,提升开发体验与稳定性
不适用场景数据更新不频繁、对实时性要求低的页面,建议仍用 HTTP 轮询或 RESTful

💡 小贴士:使用场景可以跟后端沟通,后端吊毛说用就用呗,我管你这那得。


📎 最后

如果你觉得这篇文章对你有帮助,可以 点赞 + 收藏 + 关注 支持一下,我会持续更新更多【前后端实战 + 可运行 Demo】的技术分享 ❤️!

😩😩😩摸鱼真的好无聊,只能写写水文了。