WebSocket 基础先搞懂?
想在 Vue3 项目里做实时聊天、股票行情推送这类功能,WebSocket 肯定绕不开,但刚接触时,不少同学会犯难:咋在 Vue3 里初始化连接?消息收发咋和响应式数据结合?断开重连咋处理?今天用问答方式,把 Vue3 + WebSocket 从基础到实战掰碎了讲,新手也能跟着做。
先别急着写代码,得先明白 WebSocket 是干啥的,它是 HTML5 提供的**全双工通信协议**,能让客户端和服务端随时双向发消息,和 HTTP 有啥不一样?HTTP 是“请求 - 响应”模式,服务端没法主动给客户端发消息;但 WebSocket 建立连接后,两边想发就发,特别适合实时性高的场景(比如聊天、实时数据监控)。 举个例子:你刷短视频 App 时,有人给你发评论通知,这背后可能就是 WebSocket 让服务端主动把“新评论”消息推给你,要是用 HTTP,得不停轮询(比如每隔几秒发请求问有没有新消息),既费流量又延迟高,WebSocket 就解决了这痛点。Vue3 项目里怎么初始化 WebSocket 连接?
在 Vue3 里用 WebSocket,核心是创建 WebSocket 实例,再和 Vue 的生命周期、响应式数据结合,咱分步骤看:
创建 WebSocket 实例
WebSocket 是浏览器原生 API,直接 new 就行,假设服务端地址是 ws://xxx,在 Vue3 的组件里(用组合式 API 举例),可以这么写:
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
const ws = ref(null) // 存WebSocket实例
onMounted(() => {
// 初始化连接
ws.value = new WebSocket('ws://你的服务端地址')
// 监听连接成功
ws.value.onopen = () => {
console.log('WebSocket 连接成功')
// 连接成功后可以发初始化消息,比如用户身份
ws.value.send(JSON.stringify({ type: 'login', userId: 'xxx' }))
}
// 监听连接错误
ws.value.onerror = (err) => {
console.error('WebSocket 连接出错:', err)
}
})
onUnmounted(() => {
// 组件销毁时关闭连接,避免内存泄漏
if (ws.value) {
ws.value.close()
}
})
</script>
这里用 onMounted 初始化(组件挂载后建立连接),onUnmounted 销毁时关闭连接,用 ref 存实例,是为了让 Vue 能追踪它的状态变化。
注意服务端兼容性
前端能连,得服务端也支持 WebSocket 协议,Node.js 用 ws 库、Java 用 Netty 这类框架都能搭 WebSocket 服务,要是你本地测试,也能找些公开的 WebSocket 测试地址(ws://echo.websocket.org 是经典的回声测试服务,发消息会原样返回)。
怎么处理消息收发?
连接建好了,接下来是发消息和收消息,还要把收到的消息同步到 Vue 的响应式数据里,让界面自动更新。
发消息:主动给服务端发数据
WebSocket 实例的 send() 方法负责发消息,消息可以是字符串或二进制数据,实际开发常用 JSON 字符串(方便结构化数据),比如做聊天功能,用户输入内容后发消息:
<template>
<input v-model="inputMsg" placeholder="请输入消息" />
<button @click="sendMsg">发送</button>
</template>
<script setup>
import { ref } from 'vue'
const inputMsg = ref('')
const ws = ref(null)
const sendMsg = () => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) { // 确保连接已打开
ws.value.send(JSON.stringify({
type: 'chat',
content: inputMsg.value
}))
inputMsg.value = '' // 清空输入框
} else {
alert('连接未就绪,请稍等')
}
}
</script>
收消息:服务端推消息到前端
用 onmessage 监听服务端发来的消息,收到后,把消息存到响应式数据里(messages 列表),界面就会自动更新:
<script setup>
import { ref, onMounted } from 'vue'
const messages = ref([]) // 存所有聊天消息
const ws = ref(null)
onMounted(() => {
ws.value = new WebSocket('ws://xxx')
ws.value.onmessage = (event) => {
// event.data 是服务端发来的消息
const msg = JSON.parse(event.data)
messages.value.push(msg) // 响应式数据更新,界面自动渲染
}
})
</script>
<template>
<div v-for="(msg, index) in messages" :key="index">
{{ msg.content }}
</div>
</template>
这里要注意,onmessage 里的操作要简洁,别做太耗时的事,不然影响性能,如果消息处理复杂,可以拆成函数调用。
连接状态管理有啥技巧?
WebSocket 连接很“脆弱”——网络波动、服务端重启都会导致断开,所以得做心跳保活和自动重连,让连接更稳定。
心跳机制:防止连接被网关断开
有些服务器或中间网关(比如公司内网、nginx)会把长时间没动静的连接断开,心跳就是定时给服务端发个“我还活着”的消息,保持连接。
在 Vue3 里可以用 setInterval 实现:
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
const ws = ref(null)
let heartBeatTimer = null
onMounted(() => {
ws.value = new WebSocket('ws://xxx')
// 连接成功后启动心跳
ws.value.onopen = () => {
heartBeatTimer = setInterval(() => {
if (ws.value.readyState === WebSocket.OPEN) {
ws.value.send(JSON.stringify({ type: 'heartbeat' }))
}
}, 30 * 1000) // 每30秒发一次心跳
}
})
onUnmounted(() => {
clearInterval(heartBeatTimer) // 组件销毁时清除定时器
if (ws.value) ws.value.close()
})
</script>
自动重连:断网后自动恢复连接
当连接意外断开(比如用户切到弱网环境),得自动尝试重连,可以监听 onclose 事件,结合延迟重连策略:
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
const ws = ref(null)
let reconnectTimer = null
const reconnect = () => {
reconnectTimer = setTimeout(() => {
ws.value = new WebSocket('ws://xxx')
// 重连后要重新绑定事件(比如onmessage、onopen)
ws.value.onopen = () => {
clearTimeout(reconnectTimer) // 重连成功,清除重连定时器
console.log('WebSocket 重连成功')
}
ws.value.onclose = () => {
reconnect() // 连接又断了,继续重连
}
}, 5 * 1000) // 5秒后重试
}
onMounted(() => {
ws.value = new WebSocket('ws://xxx')
ws.value.onclose = () => {
console.log('WebSocket 连接断开,开始重连')
reconnect()
}
})
onUnmounted(() => {
clearTimeout(reconnectTimer)
if (ws.value) ws.value.close()
})
</script>
这样即使网络波动,也能自动恢复连接,用户几乎没感知。
实战:做个简易实时聊天组件
光讲理论不够,咱动手做个能收发消息的聊天组件,把前面的知识点串起来。
步骤1:搭建界面结构
<template>
<div class="chat-container">
<!-- 消息列表 -->
<div class="messages">
<div
v-for="(msg, index) in messages"
:key="index"
:class="['msg-item', { self: msg.isSelf }]"
>
<span class="sender">{{ msg.sender }}:</span>
<span class="content">{{ msg.content }}</span>
</div>
</div>
<!-- 输入区域 -->
<div class="input-area">
<input
v-model="inputMsg"
placeholder="输入消息后按回车发送"
@keyup.enter="sendMsg"
/>
<button @click="sendMsg">发送</button>
</div>
</div>
</template>
步骤2:编写逻辑代码
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
// 响应式数据
const messages = ref([]) // 消息列表
const inputMsg = ref('') // 输入框内容
const ws = ref(null) // WebSocket实例
// 初始化WebSocket
onMounted(() => {
// 这里用公开测试服务,实际替换成自己的服务端地址
ws.value = new WebSocket('ws://echo.websocket.org')
// 连接成功
ws.value.onopen = () => {
console.log('连接成功,可发送消息')
// 模拟发送登录消息(实际项目传用户ID等)
ws.value.send(JSON.stringify({
type: 'login',
userId: 'vue3_user'
}))
}
// 接收消息
ws.value.onmessage = (event) => {
const res = JSON.parse(event.data)
// 测试服务会原样返回,这里模拟区分自己和对方消息
messages.value.push({
sender: res.isSelf ? '我' : '对方',
content: res.content,
isSelf: res.isSelf
})
}
// 连接错误
ws.value.onerror = (err) => {
console.error('WebSocket出错:', err)
alert('连接失败,请检查网络')
}
// 连接关闭(自动重连逻辑可参考前面的方法添加)
ws.value.onclose = () => {
console.log('连接已关闭')
}
})
// 发送消息
const sendMsg = () => {
if (!inputMsg.value.trim()) return alert('请输入内容')
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
const msg = {
type: 'chat',
content: inputMsg.value,
isSelf: true // 标记是自己发的消息
}
ws.value.send(JSON.stringify(msg))
inputMsg.value = '' // 清空输入框
} else {
alert('连接未就绪,请稍等')
}
}
// 组件销毁时关闭连接
onUnmounted(() => {
if (ws.value) {
ws.value.close()
}
})
</script>
步骤3:加简单样式(可选)
<style scoped>
.chat-container {
width: 300px;
margin: 20px auto;
border: 1px solid #eee;
border-radius: 8px;
padding: 10px;
}
.messages {
height: 300px;
overflow-y: auto;
margin-bottom: 10px;
}
.msg-item {
margin: 5px 0;
padding: 4px 8px;
border-radius: 4px;
}
.msg-item.self {
background: #e3f2fd;
margin-left: auto;
}
.input-area {
display: flex;
}
.input-area input {
flex: 1;
padding: 6px;
border: 1px solid #ddd;
border-radius: 4px 0 0 4px;
}
.input-area button {
padding: 0 16px;
border: none;
background: #42b983;
color: #fff;
border-radius: 0 4px 4px 0;
cursor: pointer;
}
</style>
现在运行组件,输入消息按回车,服务端会原样返回,消息列表里能看到自己和“对方”的消息(因为测试服务会回声,这里用isSelf标记区分),实际项目里,服务端会把消息转发给其他用户,逻辑类似。
性能和兼容性要注意啥?
代码跑通了,还要考虑生产环境的坑:
浏览器兼容性
WebSocket 是 HTML5 特性,现代浏览器(Chrome、Edge、Firefox、Safari)都支持,但老旧浏览器(IE)完全不支持,如果要兼容旧版,得用 Socket.IO 这类带 fallback 的库(但 Vue3 项目里用的话,要注意和框架的结合)。
连接数和消息大小
- 单个客户端一般别同时连太多 WebSocket 服务(浏览器对同域名的连接数有限制,Chrome 是 6 个左右)。
- 消息别太大!WebSocket 虽然后台是 TCP,但消息过大容易触发延迟或断开,建议拆分大消息,或者用二进制格式压缩。
Vue3 响应式优化
如果消息量极大(比如实时行情每秒几十条),直接往 ref 或 reactive 里 push 消息会触发频繁更新,影响性能,可以用 shallowRef 或者批量更新(比如攒一批消息再渲染)。
import { shallowRef, nextTick } from 'vue'
const messages = shallowRef([])
let tempMessages = [] // 临时数组存消息
ws.value.onmessage = (event) => {
tempMessages.push(JSON.parse(event.data))
// 每100ms批量更新一次
setTimeout(() => {
messages.value = [...tempMessages]
tempMessages = []
}, 100)
}
这样能减少响应式更新次数,提升性能。
现在再回头看,Vue3 里用 WebSocket 其实是“浏览器原生 API + Vue 响应式 + 业务逻辑”的结合,从初始化连接、收发消息,到心跳保活、自动重连,每个环节都要兼顾功能和稳定性,把今天的知识点落地到项目里,不管是做聊天、IM 还是实时数据看板,思路都通了,要是你在实践中碰到服务端配置、跨域这些问题,下次可以专门聊聊 WebSocket 服务端搭建和前端跨域处理~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。