前端零基础入门:WebSocket 全解析

大家好~ 今天给大家带来一篇「零基础也能吃透」的 WebSocket 教学文章。不同于 SSE 的单向推送,WebSocket 是双向实时通信的核心技术,也是前端进阶必备的知识点。

整篇文章全程无晦涩概念,重点解答 3 个核心问题:WebSocket 能做什么?适合哪些场景?前端+后端如何快速实现?所有代码都附带详细注释,复制就能运行,不管你是刚入门的前端新手,还是想快速落地实时功能的开发者,都能看懂、会用,直接用于项目或博客演示。

一、先搞懂:WebSocket 是什么?(超通俗解释)

在学具体用法之前,我们先搞清楚 WebSocket 的核心定位,不用记复杂定义,记住一句话就够了:

WebSocket 是一种全双工(双向)实时通信协议 ,建立连接后,客户端和服务器可以互相主动发送数据,不用客户端反复发起请求,打破了 HTTP 协议"请求-响应"的单向限制。

举个通俗的例子:HTTP 就像"打电话",必须你问一句、对方答一句,不能同时说话;而 WebSocket 就像"面对面聊天",你和对方可以随时开口,互相回应,实时性拉满。

关键区别:WebSocket vs HTTP

特性 HTTP 协议 WebSocket 协议
通信方式 单向(客户端请求,服务器响应) 双向(客户端/服务器可互相发送)
连接方式 每次请求都要重新建立连接 一次建立,长期保持连接
实时性 低(需客户端定时轮询,有延迟) 高(实时推送,无延迟)
适用场景 普通页面请求、数据查询 实时聊天、直播、弹幕等

二、WebSocket 核心应用场景(重点!知道能做什么才有用)

WebSocket 的核心优势是「实时双向通信」,以下是最常见、最实用的应用场景,新手可以对应自己的需求,快速判断是否需要用 WebSocket:

1. 实时聊天类(最经典场景)

比如微信网页版、企业IM、在线客服系统,用户发送消息后,对方能实时收到,不用刷新页面。核心需求:双方实时互发消息,延迟极低。

2. 直播相关类

直播弹幕、直播在线人数实时更新、主播与观众互动(比如送礼物提醒)。核心需求:多用户实时接收同一批数据,同时支持用户主动发送数据(发弹幕)。

3. 实时数据监控类

比如服务器监控面板、环境监测数据(温度、湿度)、金融行情(股票、数字货币实时涨跌)。核心需求:服务器实时推送数据,客户端实时展示,无需用户手动刷新。

4. 协同办公类

比如多人在线编辑文档(如腾讯文档)、共享白板、项目进度实时同步。核心需求:多用户操作后,实时同步到所有客户端,保证数据一致性。

5. 游戏类

网页小游戏(如在线五子棋、贪吃蛇),玩家的操作(移动、点击)实时同步给其他玩家和服务器,实现多人实时对战。核心需求:低延迟、高频双向数据传输。

注意:如果只需要「服务器单向推送数据」(比如 AI 流式回复、系统通知),用 SSE 更轻量;如果需要「双向通信」,优先用 WebSocket。

三、前置准备

不用准备复杂的环境,只要 3 样东西,新手轻松上手:

  1. 代码编辑器:推荐 VS Code(免费、好用,新手友好);

  2. 基础储备:懂一点点 HTML、CSS、JS(不用精通,能看懂简单代码就行);

  3. Node.js 环境:用于运行我们的简易后端(模拟 WebSocket 服务),官网下载安装即可(安装时一路下一步,不用额外配置);

  4. 额外依赖:需要安装 ws 包(Node.js 中最常用的 WebSocket 服务端库,轻量、易用)。

⚠️ 注意:本文全程用「原生 JS + Node.js」编写,不依赖 Vue、React 等任何前端框架,新手可以直接跟着敲代码,复制就能运行。

四、项目结构(极简,2 个文件搞定)

我们整个项目只有 2 个文件,结构非常简单,新建一个文件夹(比如叫 websocket-demo),里面放这两个文件即可:

复制代码
websocket-demo/
  ├─ index.html       # 前端页面(核心:聊天界面 + WebSocket 客户端逻辑)
  └─ server.js        # 后端服务(WebSocket 服务端,处理连接、消息转发)

解释:前端负责创建 WebSocket 连接、发送消息、接收消息并渲染;后端负责启动 WebSocket 服务、监听客户端连接、转发客户端之间的消息(模拟群聊效果)。

五、核心步骤 1:编写后端 WebSocket 服务

后端我们用 Node.js + ws 库实现,ws 是 Node.js 生态中最常用的 WebSocket 服务端库,轻量、API 简洁,新手容易上手。

步骤 1:安装依赖

打开 VS Code 终端,进入我们创建的 websocket-demo 文件夹,输入以下命令安装 ws 包:

复制代码
npm install ws

步骤 2:编写 server.js 代码

新建 server.js 文件,复制下面的代码,所有代码都加了详细注释,新手能看懂每一行的作用:

javascript 复制代码
// 1. 导入 ws 库(安装后才能导入,用于创建 WebSocket 服务)
const WebSocket = require('ws');

// 2. 创建 WebSocket 服务,监听 3000 端口(端口可修改,需和前端连接地址对应)
const wss = new WebSocket.Server({ port: 3000 });

// 存储所有连接的客户端(用于群聊:转发消息给所有在线用户)
const clients = new Set();

// 3. 监听客户端连接事件:当有客户端连接到服务端时触发
wss.on('connection', (ws) => {
  console.log('有新客户端连接进来了!');

  // 将新连接的客户端添加到集合中
  clients.add(ws);

  // 4. 监听客户端发送的消息:当服务端收到客户端的消息时触发
  ws.on('message', (message) => {
    console.log('收到客户端消息:', message.toString()); // 打印收到的消息(调试用)

    // 群聊逻辑:将收到的消息,转发给所有在线的客户端
    clients.forEach((client) => {
      // 检查客户端连接是否正常,正常则发送消息
      if (client.readyState === WebSocket.OPEN) {
        client.send(message.toString()); // 转发消息
      }
    });
  });

  // 5. 监听客户端断开连接事件:当客户端关闭页面/断开连接时触发
  ws.on('close', () => {
    console.log('有客户端断开连接了!');
    // 将断开连接的客户端从集合中移除(避免无效连接占用资源)
    clients.delete(ws);
  });

  // 6. 监听连接错误事件:当客户端连接出现错误时触发
  ws.on('error', (error) => {
    console.error('客户端连接出错:', error);
  });
});

console.log('WebSocket 服务已启动,监听端口:3000');
console.log('前端访问地址:http://localhost:3000/index.html');

后端代码关键说明

  1. new WebSocket.Server({ port: 3000 }):创建 WebSocket 服务,监听 3000 端口,前端连接时需要用到这个端口;

  2. wss.on('connection', (ws) => {}):核心事件,每有一个客户端连接,就会触发一次,ws 是当前连接的客户端对象,用于和该客户端通信;

  3. ws.on('message', (message) => {}):监听客户端发送的消息,message 是客户端发送的数据(默认是 Buffer 类型,需用 toString() 转成字符串);

  4. 群聊逻辑:用 Set 存储所有在线客户端,收到某一个客户端的消息后,遍历所有客户端,将消息转发出去,实现"一人发消息,所有人可见";

  5. ws.readyState === WebSocket.OPEN:判断客户端连接是否正常,只有正常连接的客户端,才能发送消息(避免报错)。

六、核心步骤 2:编写前端代码

前端用原生 JS 的 WebSocket 对象创建客户端,实现"发送消息、接收消息、渲染聊天界面",同时处理连接、断开、错误等异常情况,代码可直接复制运行。

新建 index.html 文件,复制下面的代码:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebSocket 实战:多人实时群聊(新手版)</title>
  <style>
    /* 全局样式:重置默认样式,提升界面美观度 */
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    }

    body {
      max-width: 800px; /* 限制页面宽度,避免宽屏上过于分散 */
      margin: 0 auto; /* 水平居中 */
      padding: 20px;
      background: #f5f7fa;
    }

    /* 聊天容器:显示所有消息,可滚动 */
    .chat-container {
      height: 600px;
      background: white;
      border-radius: 12px;
      padding: 20px;
      overflow-y: auto; /* 消息过多时可滚动 */
      margin-bottom: 20px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    }

    /* 消息气泡通用样式 */
    .message {
      margin-bottom: 16px;
      padding: 12px 16px;
      border-radius: 8px;
      max-width: 75%;
      line-height: 1.5;
    }

    /* 自己发送的消息:右侧,绿色背景 */
    .my-message {
      background: #009688;
      color: white;
      margin-left: auto; /* 右对齐 */
    }

    /* 其他人发送的消息:左侧,灰色背景 */
    .other-message {
      background: #e9ecef;
      color: #333;
      margin-right: auto; /* 左对齐 */
    }

    /* 系统提示:居中,浅灰色,斜体 */
    .system-message {
      text-align: center;
      color: #666;
      font-style: italic;
      margin: 10px 0;
    }

    /* 输入区域:输入框 + 发送按钮 */
    .input-area {
      display: flex; /* 弹性布局,让输入框和按钮在同一行 */
      gap: 10px; /* 输入框和按钮间距 */
    }

    #userInput {
      flex: 1; /* 输入框占满剩余空间 */
      padding: 14px 16px;
      border: 1px solid #ddd;
      border-radius: 8px;
      font-size: 16px;
      outline: none;
    }

    /* 输入框聚焦时,边框变色,提示当前处于输入状态 */
    #userInput:focus {
      border-color: #009688;
    }

    button {
      padding: 14px 24px;
      background: #009688;
      color: white;
      border: none;
      border-radius: 8px;
      cursor: pointer; /* 鼠标悬浮显示手型 */
      font-size: 16px;
    }

    button:hover {
      opacity: 0.9; /* 悬浮时轻微变浅,提升交互感 */
    }

    /* 禁用状态按钮:灰色,不可点击 */
    button:disabled {
      background: #ccc;
      cursor: not-allowed;
    }
  </style>
</head>
<body>
  <!-- 聊天容器:显示所有消息(系统提示、自己的消息、其他人的消息) -->
  <div class="chat-container" id="chatContainer"></div>
  <!-- 输入区域:输入框 + 发送按钮 -->
  <div class="input-area">
    <input type="text" id="userInput" placeholder="请输入消息..." autocomplete="off">
    <button id="sendBtn" disabled>发送</button>
  </div>

  <script>
    // ==================== 1. 获取页面元素(DOM 操作)====================
    const chatContainer = document.getElementById('chatContainer'); // 聊天容器
    const userInput = document.getElementById('userInput'); // 用户输入框
    const sendBtn = document.getElementById('sendBtn'); // 发送按钮

    // 存储 WebSocket 连接对象(全局变量,方便后续操作)
    let ws = null;

    // 随机生成一个用户名(模拟不同用户,用于区分消息发送者)
    const username = '用户' + Math.floor(Math.random() * 1000);

    // ==================== 2. 渲染消息到页面(通用函数)====================
    // 功能:将消息添加到聊天容器,区分消息类型(系统、自己、其他人)
    // 参数1:message 消息内容
    // 参数2:type 消息类型(system:系统提示;my:自己的消息;other:其他人的消息)
    function addMessage(message, type) {
      const messageDom = document.createElement('div');
      // 根据消息类型添加对应的类名,设置不同样式
      messageDom.className = `message ${type}-message`;
      // 设置消息内容(系统提示和普通消息区分显示)
      if (type === 'system') {
        messageDom.textContent = `[系统提示] ${message}`;
      } else {
        messageDom.textContent = `[${username}] ${message}`;
      }
      // 将消息添加到聊天容器
      chatContainer.appendChild(messageDom);
      // 自动滚动到底部,让用户看到最新消息
      chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    // ==================== 3. 建立 WebSocket 连接(核心逻辑)====================
    function connectWebSocket() {
      // 1. 创建 WebSocket 客户端实例,连接后端服务
      // 连接地址格式:ws://ip:端口(后端监听的是 3000 端口,所以地址是 ws://localhost:3000)
      ws = new WebSocket('ws://localhost:3000');

      // ------------------- WebSocket 核心事件监听 -------------------
      // 1. onopen:连接建立成功时触发
      ws.onopen = function () {
        console.log('WebSocket 连接建立成功!');
        addMessage('连接成功,可以发送消息啦~', 'system');
        sendBtn.disabled = false; // 启用发送按钮(连接成功才能发送消息)
      };

      // 2. onmessage:收到服务器推送的消息时触发(核心事件)
      ws.onmessage = function (event) {
        // event.data:服务器推送过来的消息内容(字符串格式)
        const receivedMsg = event.data;
        
        // 区分自己的消息和其他人的消息(自己的消息会被服务器转发回来,需要过滤)
        // 这里通过用户名判断:包含自己用户名的消息,就是自己发送的
        if (receivedMsg.includes(`[${username}]`)) {
          addMessage(receivedMsg.split(`[${username}] `)[1], 'my');
        } else {
          addMessage(receivedMsg, 'other');
        }
      };

      // 3. onclose:连接断开时触发(比如后端关闭、网络中断、页面关闭)
      ws.onclose = function (event) {
        console.log('WebSocket 连接断开:', event);
        addMessage('连接已断开,正在尝试重连...', 'system');
        sendBtn.disabled = true; // 禁用发送按钮
        // 自动重连(3 秒后重试,避免频繁重连)
        setTimeout(connectWebSocket, 3000);
      };

      // 4. onerror:连接出错时触发(比如后端未启动、端口错误)
      ws.onerror = function (error) {
        console.error('WebSocket 连接出错:', error);
        addMessage('连接出错,请检查后端是否启动!', 'system');
        sendBtn.disabled = true;
      };
    }

    // ==================== 4. 发送消息函数 ====================
    function sendMessage() {
      // 1. 获取用户输入的消息,去除首尾空格
      const message = userInput.value.trim();
      if (!message) {
        alert('请输入消息内容哦~');
        return;
      }

      // 2. 检查 WebSocket 连接是否正常(只有正常连接才能发送消息)
      if (ws.readyState !== WebSocket.OPEN) {
        alert('连接未建立,请稍等...');
        return;
      }

      // 3. 发送消息到服务器(服务器会转发给所有在线客户端)
      ws.send(message);
      // 4. 清空输入框,方便输入下一条消息
      userInput.value = '';
    }

    // ==================== 5. 绑定按钮和键盘事件(交互逻辑)====================
    // 发送按钮:点击触发发送消息
    sendBtn.addEventListener('click', sendMessage);
    // 输入框:按 Enter 键也能发送消息(提升用户体验)
    userInput.addEventListener('keydown', function (e) {
      if (e.key === 'Enter') {
        sendMessage();
      }
    });

    // ==================== 6. 初始化:页面加载完成后,建立 WebSocket 连接 ====================
    window.onload = function () {
      addMessage('正在连接服务器...', 'system');
      connectWebSocket(); // 启动连接
    };
  </script>
</body>
</html>

前端代码关键说明

  1. new WebSocket('ws://localhost:3000'):创建 WebSocket 客户端,连接后端服务,地址格式必须是ws://(WebSocket 协议),端口和后端一致;

  2. 四个核心事件(必须掌握):

    1. onopen:连接成功触发,启用发送按钮;

    2. onmessage:接收服务器消息触发,渲染消息到页面,区分自己和其他人的消息;

    3. onclose:连接断开触发,自动重连(提升用户体验);

    4. onerror:连接出错触发,提示用户检查后端。

  3. ws.readyState:判断连接状态,WebSocket.OPEN 表示连接正常,只有正常状态才能发送消息;

  4. 自动重连:连接断开后,用 setTimeout 3 秒后重新连接,避免频繁重连占用资源。

七、运行项目

代码写好后,我们来运行项目,看看实时群聊效果,步骤非常简单,跟着做就行:

步骤 1:启动后端 WebSocket 服务

  1. 打开 VS Code,打开 websocket-demo 文件夹;

  2. 打开 VS Code 终端(顶部菜单:终端 → 新建终端);

  3. 在终端中输入命令:node server.js

  4. 如果终端显示 WebSocket 服务已启动,监听端口:3000,说明后端启动成功。

步骤 2:打开前端页面,测试实时群聊

  1. 打开浏览器(推荐 Chrome、Edge);

  2. 在地址栏输入:http://localhost:3000/index.html,按下回车;

  3. 页面显示"连接成功,可以发送消息啦~",说明 WebSocket 连接建立成功;

  4. 再打开一个浏览器窗口(或标签页),同样输入 http://localhost:3000/index.html

  5. 在其中一个窗口输入消息,点击发送,两个窗口都会实时显示消息(群聊效果),完美实现双向实时通信!

常见问题排查

如果运行失败,大概率是以下 4 个问题,对照排查即可快速解决:

  • 问题 1:终端输入 node server.js 报错"Cannot find module 'ws'" → 忘记安装 ws 依赖,在终端输入 npm install ws 安装即可;

  • 问题 2:前端页面提示"连接出错,请检查后端是否启动" → 后端未启动,或后端端口被占用,重新启动后端,或修改后端端口(需和前端连接地址对应);

  • 问题 3:发送消息无反应 → 检查 WebSocket 连接状态,确保 ws.readyState === WebSocket.OPEN,或刷新页面重新连接;

  • 问题 4:多个窗口无法互相接收消息 → 检查后端代码中"群聊逻辑",确保 clients 集合正确添加/删除客户端,且消息转发逻辑无误。

八、WebSocket 进阶:还能做什么?

我们实现的是基础群聊功能,基于这个核心逻辑,你可以拓展出更多实用功能,落地到实际项目中:

1. 优化聊天功能

  • 添加用户登录(替换随机用户名,显示真实昵称);

  • 支持发送图片、表情(将图片转成 Base64 格式发送,前端解析显示);

  • 添加消息时间戳(每条消息显示发送时间);

  • 实现私聊功能(后端根据用户名转发消息,只推送给指定用户)。

2. 实现其他场景功能

  • 直播弹幕:前端发送弹幕消息,后端转发给所有在线用户,前端接收后渲染到弹幕容器;

  • 实时数据监控:后端定时获取监控数据(如服务器CPU、内存),主动推送给前端,前端实时渲染图表;

  • 在线人数统计:后端通过 clients.size 获取在线客户端数量,实时推送给所有前端,显示在线人数;

  • 多人在线游戏:前端发送玩家操作(如移动、点击),后端处理游戏逻辑,再将更新后的游戏状态推送给所有玩家。

3. 生产环境优化

  • 添加身份验证(连接时携带 token,后端验证用户合法性,避免非法连接);

  • 处理消息重发(网络波动时,消息发送失败后自动重发);

  • 使用更稳定的 WebSocket 库(如 socket.io,封装了更多功能,支持断线重连、房间管理等);

  • 部署到服务器(如阿里云、腾讯云),配置域名和 SSL,使用 wss:// 协议(加密连接,更安全)。

九、总结

到这里,你已经吃透了 WebSocket 的核心用法,总结一下重点,方便你记忆和后续回顾:

  1. WebSocket 是双向实时通信协议,一次连接,双向互发数据,实时性远高于 HTTP 轮询;

  2. 核心应用场景:实时聊天、直播弹幕、数据监控、协同办公、在线游戏等(需要双向通信的场景);

  3. 前端:用原生 WebSocket 对象创建连接,监听 onopen、onmessage、onclose、onerror 四个核心事件;

  4. 后端:用 ws 库创建服务,监听客户端连接、消息发送,实现消息转发等逻辑;

  5. 实战重点:连接状态判断、自动重连、消息转发、异常处理,这些是落地项目的关键。

其实 WebSocket 没有想象中复杂,核心就是"建立一次连接,双向自由通信",只要掌握了本文的基础代码和逻辑,就能轻松拓展出各种实时功能。

如果觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ 有任何问题,评论区留言,我会一一回复!

相关推荐
stars-he3 小时前
Silvaco 仿真NMOS 差分对器件剖面结构图
笔记·学习
2501_940041743 小时前
全栈开发实战:5个高复杂度后端集成场景
前端
蝎子莱莱爱打怪3 小时前
👋🏻👋🏻再见,拉勾网——那个"最懂互联网人"的招聘平台倒了😭
前端·后端·招聘
清平乐的技术专栏3 小时前
【FlinkSQL笔记】(二)Flink SQL 基础语法详解
笔记·sql·flink
Dontla3 小时前
(小浪)LangGraph多Agent教程笔记(多智能体)
笔记
kobesdu3 小时前
【ROS2实战笔记-23】参数系统中的动态参数与远程加载安全剖析
笔记·安全·slam·ros2
weixin_437918963 小时前
前端String 数组和Math API大全
前端·javascript
阿正的梦工坊3 小时前
【Typescript】03-函数对象与接口
前端·javascript·typescript
前端不太难3 小时前
如何优化鸿蒙 App 的启动速度?
华为·状态模式·harmonyos