在前端实时开发场景中,WebSocket 绝对是绕不开的核心技术 ------ 聊天对话、系统通知推送、实时数据监控,都离不开它的支持。但原生 WebSocket 使用起来满是痛点:断线后不会自动重连、需要手动写心跳检测、多组件复用代码冗余、页面刷新消息直接丢失......
今天就给大家带来一套Vue2/Vue3 通用的 WebSocket 完整封装方案,从适合小项目的快速入门版,到中大型项目的工具类版,再到企业级开发的全局插件终极版,层层递进,所有代码均可直接复制到项目中使用,解决 WebSocket 开发的所有核心痛点!

一、封装思路总览
不同项目规模对应不同的封装方式,按需选择即可,核心功能全覆盖:
-
直接在组件内使用:适合小项目 / Demo,快速实现基础通信
-
封装为工具类:适合中大型项目,支持心跳 + 断线重连,方便复用
-
封装为全局插件 + 结合 Pinia/Vuex:适合企业级项目,全局状态管理、消息类型分发、离线消息缓存,一站式解决
二、入门版:组件内直接使用(快速上手)
如果只是做小 Demo 或者简单的实时功能,无需复杂封装,直接在 Vue 组件内初始化 WebSocket 即可,实现基础的连接、发消息、收消息功能。
完整可运行代码
javascript
<template>
<div>
<h2>WebSocket 基础通信示例</h2>
<button @click="sendMessage">发送测试消息</button>
<p>服务端返回:{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
socket: null, // 存储WebSocket实例
message: "" // 存储接收的消息
};
},
created() {
// 组件创建时初始化连接
this.initWebSocket();
},
beforeDestroy() {
// 组件销毁时关闭连接,防止内存泄漏
this.socket && this.socket.close();
},
methods: {
initWebSocket() {
// 替换为自己的WebSocket服务端地址
this.socket = new WebSocket("ws://localhost:8080");
// 连接成功回调
this.socket.onopen = () => {
console.log("WebSocket 连接成功");
};
// 接收消息回调
this.socket.onmessage = (event) => {
this.message = event.data;
};
// 连接关闭回调
this.socket.onclose = () => {
console.log("WebSocket 已关闭");
};
// 连接错误回调
this.socket.onerror = (err) => {
console.error("WebSocket 连接错误", err);
};
},
sendMessage() {
// 确认连接处于打开状态再发送消息
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send("Hello Server");
}
}
}
};
</script>
核心要点:
-
组件生命周期联动:
created初始化连接,beforeDestroy关闭连接,避免内存泄漏 -
发送消息前做状态判断:确保 WebSocket 处于
OPEN状态,防止消息发送失败 -
极简实现:仅保留核心通信逻辑,适合快速验证功能
三、进阶版:工具类封装(心跳 + 断线重连,中大型项目必备)
小项目的快速实现方案无法满足中大型项目的复用和稳定性需求,这时候就需要将 WebSocket 封装为工具类,核心实现心跳检测 和自动断线重连,解决网络波动、服务端临时断开的问题。
第一步:封装 WebSocket 工具类
创建/utils/websocket.js,封装通用的 WebSocket 逻辑,支持自定义心跳间隔、重连间隔:
javascript
class WS {
constructor(url, options = {}) {
this.url = url; // 服务端地址
this.ws = null; // WebSocket实例
this.lockReconnect = false; // 重连锁,防止重复重连
// 自定义心跳间隔,默认5000ms
this.heartbeatInterval = options.heartbeatInterval || 5000;
// 自定义重连间隔,默认3000ms
this.reconnectInterval = options.reconnectInterval || 3000;
this.heartbeatTimer = null; // 心跳定时器
this.reconnectTimer = null; // 重连定时器
this.onMessage = options.onMessage || function () {}; // 自定义收消息回调
this.init(); // 初始化连接
}
// 初始化WebSocket
init() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log("WebSocket 连接成功");
this.startHeartbeat(); // 连接成功后开启心跳
};
this.ws.onmessage = (event) => {
this.onMessage(event.data); // 执行自定义收消息回调
this.startHeartbeat(); // 收到消息后重置心跳,防止误判断线
};
this.ws.onclose = () => {
console.log("WebSocket 已关闭");
this.reconnect(); // 关闭后自动重连
};
this.ws.onerror = () => {
console.error("WebSocket 出错");
this.reconnect(); // 出错后自动重连
};
}
// 发送消息
send(msg) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(msg);
} else {
console.warn("WebSocket 未连接,消息未发送");
}
}
// 心跳检测:定时发送ping,确认连接有效性
startHeartbeat() {
clearTimeout(this.heartbeatTimer);
this.heartbeatTimer = setTimeout(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send("ping");
}
}, this.heartbeatInterval);
}
// 自动重连:加锁防止多次重连
reconnect() {
if (this.lockReconnect) return;
this.lockReconnect = true;
clearTimeout(this.reconnectTimer);
this.reconnectTimer = setTimeout(() => {
console.log("尝试重连 WebSocket...");
this.init();
this.lockReconnect = false;
}, this.reconnectInterval);
}
}
exportdefault WS;
第二步:Vue 组件中使用工具类
在组件中引入封装好的工具类,简单配置即可实现带心跳和重连的 WebSocket 通信:
javascript
<template>
<div>
<h3>WebSocket 聊天功能</h3>
<input v-model="input" placeholder="输入消息发送..." />
<button @click="send">发送</button>
<ul>
<li v-for="(msg, i) in messages" :key="i">{{ msg }}</li>
</ul>
</div>
</template>
<script>
// 引入封装的WebSocket工具类
import WS from"@/utils/websocket";
exportdefault {
data() {
return {
ws: null,
input: "",
messages: [] // 存储聊天消息
};
},
created() {
// 初始化WebSocket,配置收消息回调
this.ws = new WS("ws://localhost:8080", {
onMessage: (msg) => {
this.messages.push(msg);
}
});
},
methods: {
send() {
if (this.input.trim()) {
this.ws.send(this.input); // 调用工具类的send方法
this.messages.push("我: " + this.input);
this.input = "";
}
}
}
};
</script>
核心亮点
-
心跳检测 :定时发送
ping包,服务端返回pong(无需前端处理),确认连接有效性 -
断线重连:重连锁防止多次触发重连,网络恢复后自动恢复连接
-
高度复用:工具类可在任意组件中引入,一次封装多处使用,减少代码冗余
-
自定义配置:支持自定义心跳和重连间隔,适配不同项目需求
四、终极版:全局插件封装(Vue2/3 通用 + Pinia/Vuex,企业级开发)
对于大型企业级项目,需要更完善的全局管理能力:全局注入 WebSocket 实例 、消息类型分发 、离线消息缓存 、页面刷新消息不丢失 、全局连接状态管理。
这套方案封装为 Vue 全局插件,Vue2/Vue3 通用,结合 Pinia(Vue2 可替换为 Vuex)管理全局状态,实现一站式的 WebSocket 解决方案。
前置准备
Vue3 项目需安装 Pinia:npm install pinia,并在main.js中注册;Vue2 项目可使用 Vuex,思路完全一致。
第一步:创建 Pinia Store 管理全局状态
创建/stores/wsStore.js,管理 WebSocket 的连接状态 和全局消息 ,支持按类型筛选消息,遵循 Pinia最小可变点原则,精准更新数据减少组件重渲染:
javascript
import { defineStore } from"pinia";
/**
* wsStore:管理全局WebSocket消息和连接状态
* 支持:消息存储、连接状态管理、按类型分发消息(chat/notification/system等)
*/
exportconst useWsStore = defineStore("ws", {
state: () => ({
messages: [], // 存储所有收到的消息,格式{ type: 'xxx', content: 'xxx' }
status: "closed"// 连接状态:connected/closed/error
}),
actions: {
/**
* 添加消息:默认类型为default,精准更新数组避免整体替换
* @param {Object} msgObj - 消息对象
*/
addMessage(msgObj) {
if (!msgObj.type) msgObj.type = "default";
this.messages.push(msgObj); // 最小化变更,提升性能
},
/**
* 按类型筛选消息
* @param {string} type - 消息类型
* @returns {Array} 对应类型的消息列表
*/
getMessagesByType(type) {
returnthis.messages.filter(msg => msg.type === type);
},
/**
* 设置连接状态
* @param {string} status - 连接状态
*/
setStatus(status) {
this.status = status;
},
/**
* 清空所有消息
*/
clearMessages() {
this.messages = [];
},
/**
* 从localStorage恢复消息:页面刷新后不丢失
*/
initFromStorage() {
const cacheMsg = localStorage.getItem("wsMessages");
if (cacheMsg) this.messages = JSON.parse(cacheMsg);
const cacheStatus = localStorage.getItem("wsStatus");
if (cacheStatus) this.status = cacheStatus;
},
/**
* 缓存消息到localStorage:实时同步
*/
cacheToStorage() {
localStorage.setItem("wsMessages", JSON.stringify(this.messages));
localStorage.setItem("wsStatus", this.status);
}
}
});
第二步:封装 WebSocket 全局插件
创建/plugins/websocket.js,封装为 Vue 插件,实现全局注入 、消息类型解析 、离线缓存 、心跳 + 重连,自动将消息同步到 Pinia:
javascript
import { useWsStore } from"@/stores/wsStore";
/**
* WSPlugin:Vue2/Vue3通用WebSocket全局插件
* 核心功能:全局注入$ws、心跳检测、自动重连、消息类型分发、离线缓存
*/
class WSPlugin {
constructor(url, options = {}) {
this.url = url;
this.ws = null;
this.lockReconnect = false;
this.heartbeatInterval = options.heartbeatInterval || 5000;
this.reconnectInterval = options.reconnectInterval || 3000;
this.heartbeatTimer = null;
this.reconnectTimer = null;
this.store = useWsStore(); // 引入Pinia Store
this.customMessageHandler = options.onMessage || null; // 自定义消息回调
this.init();
}
// 初始化WebSocket连接
init() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log("WebSocket 已连接");
this.store.setStatus("connected");
this.store.cacheToStorage(); // 缓存连接状态
this.startHeartbeat();
};
this.ws.onmessage = (event) => {
// 心跳响应pong不做处理
if (event.data === "pong") return;
let msgObj;
// 解析消息:默认JSON格式,解析失败则设为default类型
try {
msgObj = JSON.parse(event.data);
} catch {
msgObj = { type: "default", content: event.data };
}
// 同步消息到Pinia并缓存到本地
this.store.addMessage(msgObj);
this.store.cacheToStorage();
// 执行自定义消息回调
if (typeofthis.customMessageHandler === "function") {
this.customMessageHandler(msgObj);
}
this.startHeartbeat(); // 重置心跳
};
this.ws.onclose = () => {
console.log("WebSocket 已关闭");
this.store.setStatus("closed");
this.store.cacheToStorage();
this.reconnect();
};
this.ws.onerror = (err) => {
console.error("WebSocket 出错", err);
this.store.setStatus("error");
this.store.cacheToStorage();
this.reconnect();
};
}
// 发送消息:支持字符串/对象,对象自动序列化为JSON
send(msg) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
if (typeof msg === "object") msg = JSON.stringify(msg);
this.ws.send(msg);
} else {
console.warn("WebSocket 未连接,消息未发送");
}
}
// 心跳检测
startHeartbeat() {
clearTimeout(this.heartbeatTimer);
this.heartbeatTimer = setTimeout(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send("ping");
}
}, this.heartbeatInterval);
}
// 自动重连
reconnect() {
if (this.lockReconnect) return;
this.lockReconnect = true;
clearTimeout(this.reconnectTimer);
this.reconnectTimer = setTimeout(() => {
console.log("尝试重连 WebSocket...");
this.init();
this.lockReconnect = false;
}, this.reconnectInterval);
}
// 手动关闭连接
close() {
clearTimeout(this.heartbeatTimer);
clearTimeout(this.reconnectTimer);
this.ws && this.ws.close();
}
}
// Vue插件安装方法:Vue2/Vue3通用
exportdefault {
install(app, options) {
const wsInstance = new WSPlugin(options.url, options);
// Vue3 全局挂载
if (app.config) {
app.config.globalProperties.$ws = wsInstance;
} else {
// Vue2 全局挂载
app.prototype.$ws = wsInstance;
}
}
};
第三步:全局注册插件(main.js)
在 Vue 入口文件中注册 Pinia 和 WebSocket 插件,全局注入 $ws 实例:
javascript
import { createApp } from"vue";
import { createPinia } from"pinia";
import App from"./App.vue";
// 引入WebSocket全局插件
import WSPlugin from"./plugins/websocket";
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
// 安装WebSocket插件,配置服务端地址和自定义参数
app.use(WSPlugin, {
url: "ws://localhost:8080", // 替换为自己的服务端地址
heartbeatInterval: 5000,
reconnectInterval: 3000,
// 全局自定义消息回调
onMessage: (msg) => {
console.log("全局收到消息:", msg);
}
});
app.mount("#app");
第四步:Vue 组件中使用全局插件
组件中无需再手动初始化 WebSocket,直接使用$ws发送消息,从 Pinia 中获取消息和连接状态,支持按类型筛选,页面刷新后消息自动恢复:
javascript
<template>
<div>
<h3>全局WebSocket - 聊天&通知</h3>
<!-- 聊天消息 -->
<div class="chat">
<h4>聊天消息</h4>
<ul>
<li v-for="(msg, i) in chatMessages" :key="i">{{ msg.content }}</li>
</ul>
</div>
<!-- 系统通知 -->
<div class="notification">
<h4>系统通知</h4>
<ul>
<li v-for="(msg, i) in notifications" :key="i">{{ msg.content }}</li>
</ul>
</div>
<!-- 发送消息 -->
<input v-model="input" placeholder="输入聊天消息..." />
<button @click="sendChat">发送聊天</button>
<button @click="clearAll">清空所有消息</button>
<!-- 连接状态 -->
<p>当前连接状态:{{ status }}</p>
</div>
</template>
<script>
import { ref, computed, onMounted } from"vue";
import { useWsStore } from"@/stores/wsStore";
exportdefault {
setup() {
const wsStore = useWsStore();
const input = ref("");
// 页面加载时从localStorage恢复消息
onMounted(() => {
wsStore.initFromStorage();
});
// 按类型筛选消息:聊天消息
const chatMessages = computed(() => wsStore.getMessagesByType("chat"));
// 按类型筛选消息:系统通知
const notifications = computed(() => wsStore.getMessagesByType("notification"));
// 连接状态
const status = computed(() => wsStore.status);
// 发送聊天消息:带类型,服务端可按类型分发
const sendChat = () => {
if (!input.value.trim()) return;
// 调用全局$ws发送消息
wsStore.$root?.$ws.send({ type: "chat", content: input.value });
// 本地添加消息
wsStore.addMessage({ type: "chat", content: "我: " + input.value });
input.value = "";
};
// 清空所有消息
const clearAll = () => {
wsStore.clearMessages();
wsStore.cacheToStorage();
};
return {
input,
chatMessages,
notifications,
status,
sendChat,
clearAll
};
}
};
</script>
终极版核心优势
-
Vue2/Vue3 通用:一套代码适配两个 Vue 版本,降低迁移成本
-
全局注入 :所有组件可直接使用
$ws发送消息,无需重复引入 -
消息类型分发 :按
type字段区分聊天、通知、系统广播,组件按需渲染 -
离线消息缓存 :消息和连接状态同步到
localStorage,页面刷新不丢失 -
全局状态管理:Pinia 统一管理连接状态和消息,多组件数据同步
-
性能优化:遵循 Pinia 最小可变点原则,精准更新数据减少组件重渲染
-
高度可扩展:支持自定义消息回调、心跳 / 重连间隔,适配各种业务场景
五、实用优化 & 避坑指南
🔧 前端优化
-
鉴权处理:建立连接时携带 token,保证通信安全
-
方式 1:拼接在 url 后:
ws://localhost:8080?token=xxx -
方式 2:通过子协议 header 传递(适合复杂鉴权)
-
-
存储优化 :消息量大时,用
IndexedDB替代localStorage,提升存储性能和容量 -
消息去重 :聊天应用可结合用户 ID + 消息唯一 ID,避免离线重连后重复接收消息
-
消息过期:添加消息过期机制,定期清理本地缓存,防止缓存无限增长
-
重连限制:可添加最大重连次数,避免网络彻底断开时无限重连消耗资源
🔧 服务端配合
-
心跳响应 :服务端收到
ping包后,及时返回pong包,确认连接有效性 -
高并发优化 :配合消息队列 + 负载均衡,避免单点 WebSocket 服务压力过大
-
离线消息推送:服务端记录用户离线消息,重连后主动推送,保证消息不丢失
-
连接管理:服务端定期清理无效连接,释放服务器资源
⚠️ 常见坑点
-
重连锁缺失 :未加锁会导致多次触发重连,创建多个 WebSocket 实例,必须添加
lockReconnect -
组件销毁未关连接 :Vue 组件销毁时未关闭 WebSocket,会导致内存泄漏,需在
beforeDestroy中处理 -
发送消息未判断状态 :连接未打开时发送消息会失败,必须判断
readyState === WebSocket.OPEN -
Pinia/Vuex 数据更新不及时:避免直接修改状态,使用 actions 精准更新,遵循最小可变点原则
六、场景选型建议
|-------------------|----------------------------|--------------------------|
| 封装方式 | 适用场景 | 核心优势 |
| 组件内直接使用 | 小项目 / Demo、简单实时功能 | 快速上手、代码量少 |
| 工具类封装 | 中大型项目、多组件复用 WebSocket | 心跳 + 重连、高度复用、自定义配置 |
| 全局插件 + Pinia/Vuex | 企业级项目、复杂实时业务(聊天 / 推送 / 监控) | 全局管理、消息分发、离线缓存、Vue2/3 通用 |
最后
以上就是 Vue2/Vue3 完整的 WebSocket 封装方案,所有代码均可直接复制到项目中使用,解决了实时开发中 WebSocket 的绝大部分痛点。从快速入门到企业级实现,层层递进,大家可以根据自己的项目规模按需选择。