大家好~ 今天给大家带来一篇「零基础也能吃透」的 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 样东西,新手轻松上手:
-
代码编辑器:推荐 VS Code(免费、好用,新手友好);
-
基础储备:懂一点点 HTML、CSS、JS(不用精通,能看懂简单代码就行);
-
Node.js 环境:用于运行我们的简易后端(模拟 WebSocket 服务),官网下载安装即可(安装时一路下一步,不用额外配置);
-
额外依赖:需要安装
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');
后端代码关键说明
-
new WebSocket.Server({ port: 3000 }):创建 WebSocket 服务,监听 3000 端口,前端连接时需要用到这个端口; -
wss.on('connection', (ws) => {}):核心事件,每有一个客户端连接,就会触发一次,ws是当前连接的客户端对象,用于和该客户端通信; -
ws.on('message', (message) => {}):监听客户端发送的消息,message是客户端发送的数据(默认是 Buffer 类型,需用toString()转成字符串); -
群聊逻辑:用
Set存储所有在线客户端,收到某一个客户端的消息后,遍历所有客户端,将消息转发出去,实现"一人发消息,所有人可见"; -
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>
前端代码关键说明
-
new WebSocket('ws://localhost:3000'):创建 WebSocket 客户端,连接后端服务,地址格式必须是ws://(WebSocket 协议),端口和后端一致; -
四个核心事件(必须掌握):
-
onopen:连接成功触发,启用发送按钮; -
onmessage:接收服务器消息触发,渲染消息到页面,区分自己和其他人的消息; -
onclose:连接断开触发,自动重连(提升用户体验); -
onerror:连接出错触发,提示用户检查后端。
-
-
ws.readyState:判断连接状态,WebSocket.OPEN表示连接正常,只有正常状态才能发送消息; -
自动重连:连接断开后,用
setTimeout3 秒后重新连接,避免频繁重连占用资源。
七、运行项目
代码写好后,我们来运行项目,看看实时群聊效果,步骤非常简单,跟着做就行:
步骤 1:启动后端 WebSocket 服务
-
打开 VS Code,打开
websocket-demo文件夹; -
打开 VS Code 终端(顶部菜单:终端 → 新建终端);
-
在终端中输入命令:
node server.js; -
如果终端显示
WebSocket 服务已启动,监听端口:3000,说明后端启动成功。
步骤 2:打开前端页面,测试实时群聊
-
打开浏览器(推荐 Chrome、Edge);
-
在地址栏输入:
http://localhost:3000/index.html,按下回车; -
页面显示"连接成功,可以发送消息啦~",说明 WebSocket 连接建立成功;
-
再打开一个浏览器窗口(或标签页),同样输入
http://localhost:3000/index.html; -
在其中一个窗口输入消息,点击发送,两个窗口都会实时显示消息(群聊效果),完美实现双向实时通信!
常见问题排查
如果运行失败,大概率是以下 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 的核心用法,总结一下重点,方便你记忆和后续回顾:
-
WebSocket 是双向实时通信协议,一次连接,双向互发数据,实时性远高于 HTTP 轮询;
-
核心应用场景:实时聊天、直播弹幕、数据监控、协同办公、在线游戏等(需要双向通信的场景);
-
前端:用原生
WebSocket对象创建连接,监听onopen、onmessage、onclose、onerror四个核心事件; -
后端:用
ws库创建服务,监听客户端连接、消息发送,实现消息转发等逻辑; -
实战重点:连接状态判断、自动重连、消息转发、异常处理,这些是落地项目的关键。
其实 WebSocket 没有想象中复杂,核心就是"建立一次连接,双向自由通信",只要掌握了本文的基础代码和逻辑,就能轻松拓展出各种实时功能。
如果觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ 有任何问题,评论区留言,我会一一回复!