如何使用vue3簡單實現(xiàn)WebSocket通信
前言
WebSocket是一種在客戶端和服務(wù)器之間進(jìn)行雙向通信的網(wǎng)絡(luò)協(xié)議。它通過建立持久性的、全雙工的連接,允許服務(wù)器主動向客戶端發(fā)送數(shù)據(jù),而不需要客戶端顯式地發(fā)送請求。
關(guān)于WebSocket通信的簡單介紹:
- 握手階段:在建立WebSocket連接之前,客戶端需要發(fā)送一個HTTP請求到服務(wù)器,請求升級為WebSocket協(xié)議。這個過程稱為握手(Handshake)。如果服務(wù)器支持WebSocket協(xié)議,它將返回帶有特定標(biāo)頭的HTTP響應(yīng),表示握手成功。
- 建立連接:客戶端收到服務(wù)器的握手響應(yīng)后,會重新建立連接。此時,連接將從HTTP協(xié)議切換到WebSocket協(xié)議,并保持打開狀態(tài)。這樣就建立了可持續(xù)的雙向通信通道。
- 數(shù)據(jù)傳輸:一旦WebSocket連接建立,客戶端和服務(wù)器可以開始互相發(fā)送消息??蛻舳撕头?wù)器都可以通過發(fā)送文本或二進(jìn)制數(shù)據(jù)來通信。消息可以是簡單的字符串,也可以是復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如JSON對象等。
- 斷開連接:當(dāng)需要關(guān)閉WebSocket連接時,客戶端或服務(wù)器可以主動發(fā)送一個關(guān)閉幀來斷開連接。收到關(guān)閉幀的一方會結(jié)束連接并發(fā)送回應(yīng)幀,完成連接的關(guān)閉。
WebSocket通信具有以下特點:
- 實時性:由于WebSocket使用長連接,可以實時地將數(shù)據(jù)推送到客戶端,而不需要顯式地發(fā)送請求。這使得它非常適合需要快速、實時響應(yīng)的應(yīng)用程序。
- 雙向通信:WebSocket允許客戶端和服務(wù)器之間雙向傳輸消息。這意味著服務(wù)器可以主動向客戶端推送數(shù)據(jù),而不僅限于響應(yīng)客戶端的請求。
- 較低的開銷:相比于傳統(tǒng)的輪詢方式(每隔一段時間發(fā)送請求),WebSocket連接具有較低的開銷。一旦建立連接,它只需要發(fā)送少量的數(shù)據(jù)頭部信息,并且在保持連接時可以重復(fù)使用該連接。
- 跨平臺支持:WebSocket協(xié)議是一種標(biāo)準(zhǔn)化的協(xié)議,被廣泛支持和應(yīng)用于不同的平臺和編程語言中。
通過使用WebSocket,開發(fā)人員可以實現(xiàn)實時通信、聊天應(yīng)用、多人游戲、股票行情等需要及時交互和更新的應(yīng)用程序。
相關(guān)代碼如下:
①創(chuàng)建src/utils/websocket.ts文件
import { ElMessage } from 'element-plus' import store from '../store' let websocket: WebSocket | null = null; // 用于存儲實例化后websocket let rec: any; // 斷線重連后,延遲5秒重新創(chuàng)建WebSocket連接 rec用來存儲延遲請求的代碼 // 創(chuàng)建websocket function creatWebSocket(wsUrl: string) { console.log("websocket=================="); // 判斷當(dāng)前瀏覽器是否支持WebSocket if ("WebSocket" in window) { console.log("當(dāng)前瀏覽器支持 WebSocket"); } else if ("MozWebSocket" in window) { console.log("當(dāng)前瀏覽器支持 MozWebSocket"); } else { console.log("當(dāng)前瀏覽器不支持 WebSocket"); } try { initWebSocket(wsUrl); // 初始化websocket連接 } catch (e) { console.log("嘗試創(chuàng)建連接失敗"); reConnect(wsUrl); // 如果無法連接上 webSocket 那么重新連接!可能會因為服務(wù)器重新部署,或者短暫斷網(wǎng)等導(dǎo)致無法創(chuàng)建連接 } } // 初始化websocket function initWebSocket(wsUrl: string) { websocket = new WebSocket(wsUrl); console.log("websocket:", websocket); websocket.onopen = function () { websocketOpen(); }; // // 接收 websocket.onmessage = function (e: MessageEvent<any>) { websocketonmessage(e); }; // 連接發(fā)生錯誤 websocket.onerror = function () { console.log("WebSocket連接發(fā)生錯誤"); // isConnect = false; // 連接斷開修改標(biāo)識 reConnect(wsUrl); // 連接錯誤 需要重連 }; websocket.onclose = function (e) { websocketclose(e); }; } // 定義重連函數(shù) let reConnect = (wsUrl: string) => { console.log("嘗試重新連接"); if (store.state.isConnected) return; // 如果已經(jīng)連上就不在重連了 rec && clearTimeout(rec); rec = setTimeout(function () { // 延遲5秒重連 避免過多次過頻繁請求重連 creatWebSocket(wsUrl); }, 5000); }; // 創(chuàng)建連接 function websocketOpen() { console.log("連接成功"); store.dispatch('connect'); // 修改連接狀態(tài) } // 數(shù)據(jù)接收 function websocketonmessage(e: MessageEvent<any>) { console.log("數(shù)據(jù)接收", e.data); const data = JSON.parse(e.data); // 解析JSON格式的數(shù)據(jù) // 下面的判斷則是后臺返回的接收到的數(shù)據(jù) 如何處理自己決定 if (data.code === 400) { console.log("數(shù)據(jù)接收", data.msg); ElMessage({ showClose: true, message: data.msg, type: 'warning', }) } else if (data.code === 404) { ElMessage({ showClose: true, message: data.msg, type: 'warning', }) } else if (data.code === 0) { ElMessage({ showClose: true, message: "連接成功", type: 'success', }) } else if (data.code === 200) { ElMessage({ showClose: true, message: data.msg, type: 'success', }) // 成功后的相應(yīng)處理 此處成功后播放音樂 const audio = new Audio('./tipMusic.mp3'); audio.play(); } else { ElMessage({ showClose: true, message: data.msg, type: 'error', }) // 延時5秒后刷新頁面 setTimeout(() => { location.reload(); }, 1000); } // let data = JSON.parse(decodeUnicode(e.data)) } // 關(guān)閉 function websocketclose(e: any) { console.log(e); store.dispatch('disconnect'); // 修改連接狀態(tài) console.log("connection closed (" + e.code + ")"); } // 數(shù)據(jù)發(fā)送 function websocketsend(data: any) { console.log("發(fā)送的數(shù)據(jù)", data, JSON.stringify(data)); if (websocket && store.state.isConnected) { // 檢查連接狀態(tài) websocket.send(JSON.stringify(data)); } else { ElMessage({ showClose: true, message: "請選擇設(shè)備連接", type: 'error', }) } } // 實際調(diào)用的方法============== // 發(fā)送 function sendWebSocket(data: any) { // 如果未保持連接狀態(tài) 不允許直接發(fā)送消息 提示請選擇連接設(shè)備 if (!store.state.isConnected) { ElMessage({ showClose: true, message: "請選擇設(shè)備連接", type: 'error', }) } else { websocketsend(data); console.log("------------------"); } } // 關(guān)閉 let closeWebSocket = () => { if (websocket) { websocket.close(); ElMessage({ showClose: true, message: "設(shè)備已關(guān)閉", type: 'success', }) } }; export { initWebSocket, sendWebSocket, creatWebSocket, closeWebSocket, };
②全局保存連接狀態(tài)src/store/index.ts
先安裝npm install vuex
import { createStore } from 'vuex' const store = createStore({ state: { isConnected: false,//連接狀態(tài) }, mutations: { setConnected(state: any, isConnected: boolean) { state.isConnected = isConnected }, }, actions: { connect({ commit }: { commit: any }) { // 連接成功后,將 isConnected 狀態(tài)設(shè)置為 true commit('setConnected', true) }, disconnect({ commit }: { commit: any }) { // 斷開連接或退出登錄時,將 isConnected 狀態(tài)設(shè)置為 false commit('setConnected', false) } } }) export default store
③頁面連接設(shè)備
<script setup lang="ts"> import { ref ,onMounted} from 'vue' import { closeWebSocket,initWebSocket } from '../../utils/websocket' import { useStore } from 'vuex'; const store = useStore() //連接設(shè)備 (具體路徑和后端規(guī)定) function connectMsg() { const toIp = `ws://192.168.50.50:8822/websocket/ipad/${roomId.value}`; store.dispatch('connect') initWebSocket(toIp) } // 設(shè)備斷開 function closeWs() { closeWebSocket() store.dispatch('disconnect') } </script>
<template> <div class="connect"> ? ? ? ? ? <el-button class="elbtn" @click="connectMsg">連接設(shè)備</el-button> ? ? ? ? <el-button class="elbtn" @click="closeWs">關(guān)閉設(shè)備</el-button> ? ? ? ? </div> </template>
④發(fā)送消息給后端
<script setup lang="ts"> import {ref} from 'vue' import { sendWebSocket } from '../../utils/websocket' const courseTopic = ref('') const courseGrowth = ref('') const todayHaul = ref('') const selectedTeacher = ref('') //提交 const harvestSubmit = () => { ? // 要發(fā)送的數(shù)據(jù)? 和后端定義格式 ? const harvestData = { ? ? "HandlerType": "COURSEREFLECT", ? ? "topicReflect": courseTopic.value, ? ? "growReflect": courseGrowth.value, ? ? "harvest": todayHaul.value, ? ? "teacher": "李老師" } ? ? console.log("提交反思與收獲數(shù)據(jù)",harvestData);? ? // 發(fā)送消息給后端 ? ? sendWebSocket(harvestData) ? } </script> <template> ? ? ? ? <div class="think"> ? ? ? ? ? <p>課程反思</p> ? ? ? ? <el-input v-model="courseTopic" :rows="3" type="textarea" placeholder="主題課程" /> ? ? ? ? <el-input v-model="courseGrowth" :rows="3" type="textarea" placeholder="適性成長課程" /> ? ? ? ? <p>今日收獲</p> ? ? ? ? <el-input v-model="todayHaul" :rows="3" type="textarea" ?/> ? ? ? ? </div> ? ? ? ? <div class="btn"> ? ? ? ? ? ? <button @click="harvestSubmit"> 提交</button> ? ? ? ? ? </div> ? ? </template>
總結(jié)
到此這篇關(guān)于如何使用vue3簡單實現(xiàn)WebSocket通信的文章就介紹到這了,更多相關(guān)vue3實現(xiàn)WebSocket通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中for循環(huán)更改數(shù)據(jù)的實例代碼(數(shù)據(jù)變化但頁面數(shù)據(jù)未變)
這篇文章主要介紹了vue中for循環(huán)更改數(shù)據(jù)的實例代碼(數(shù)據(jù)變化但頁面數(shù)據(jù)未變)的相關(guān)資料,需要的朋友可以參考下2017-09-09VUE動態(tài)綁定class類的三種常用方式及適用場景詳解
文章介紹了在實際開發(fā)中動態(tài)綁定class的三種常見情況及其解決方案,包括根據(jù)不同的返回值渲染不同的class樣式、給模塊添加基礎(chǔ)樣式以及根據(jù)設(shè)計確定是否使用多個樣式2025-01-01Vue報錯:TypeError:Cannot create property '
這篇文章主要介紹了Vue報錯:TypeError:Cannot create property 'xxx' on string 'xxxx'問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Vue項目打包問題詳解(生產(chǎn)環(huán)境樣式失效)
在Vue開發(fā)過程中,項目的打包是一個非常重要的步驟,下面這篇文章主要給大家介紹了關(guān)于Vue項目打包問題(生產(chǎn)環(huán)境樣式失效)的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12淺談Vue render函數(shù)在ElementUi中的應(yīng)用
今天小編就為大家分享一篇淺談Vue render函數(shù)在ElementUi中的應(yīng)用,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09