node+vue3的websocket前后端消息推送

node+vue3的websocket前后端消息推送

前期写web项目时,前端获取数据的方式一般是向后端发起数据请求,然后后端向前端发送数据,然后对数据进行渲染,这是最常规的一种数据通讯方式,适用于绝大部分前后端分离的项目

实际上还有一种场景,需要后端向前端主动发送数据,然后前端更新视图,这种一般出现在数据大屏显示项目中。后端每间隔一段时间向前端发送数据,然后驱动前端更新页面,使用的技术是websocket,研究了一段时间,算搞明白是怎么回事了

先看看最终前端的展示效果

在这里插入图片描述

一、后端使用websocket

这里我用的后端是node,服务框架用的是express

首先安装express和ws,我的安装包信息如下:

{"name": "api","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC","dependencies": {"express": "^4.19.2","ws": "^8.16.0"}
}

1、websocket服务

新建service/websocket_service.js文件,代码如下

const WebSocket = require("ws");
const { getProvinceData, getBarData } = require("../utils/getData");
const wss = new WebSocket.Server({ port: 9998 });module.exports.listen = () => {wss.on("connection", (ws, req) => {console.log("WebSocket client connected");ws.on("close", () => {console.log("WebSocket client disconnected");});ws.on("message", (data) => {console.log(`Received message from client: ${data}`);if (data == "map") {ws.send(JSON.stringify({data: getProvinceData(),}));}if (data == "bar") {ws.send(JSON.stringify({data: getBarData(),}));}});});
};

这个文件是websocket的关键,后面会在后端入口文件app.js中引用。websocket连接成功后,会监听连接事件、关闭事件、接收消息事件,接收消息事件中,根据前端发送过来的字段,向前端推送不同的数据。正常情况,这里应该是从数据库取数据,而非随机数据,数据库取数据的逻辑写过无数遍,换一下代码就行了。

此外,还引用了一个随机获取数据的文件getData.js,

代码如下:

const province = [{ name: "山东", value: 2706 },{ name: "江苏", value: 2321 },{ name: "广东", value: 2108 },{ name: "河南", value: 2081 },{ name: "四川", value: 1878 },{ name: "湖南", value: 1760 },{ name: "河北", value: 1659 },{ name: "湖北", value: 1641 },{ name: "安徽", value: 1411 },{ name: "江西", value: 1340 },{ name: "云南", value: 1195 },{ name: "广西", value: 1189 },{ name: "贵州", value: 1135 },{ name: "辽宁", value: 1068 },{ name: "浙江", value: 1030 },{name: "陕西",value: 727,},{name: "山西",value: 720,},{name: "福建",value: 714,},{name: "重庆",value: 589,},{name: "甘肃",value: 522,},{name: "内蒙古",value: 485,},{name: "吉林",value: 458,},{name: "黑龙江",value: 452,},{name: "新疆",value: 445,},{name: "上海",value: 267,},{name: "海南",value: 216,},{name: "北京",value: 213,},{name: "天津",value: 204,},{name: "宁夏",value: 171,},{name: "青海",value: 121,},{ name: "西藏", value: 85 },
];exports.getProvinceData = () => {const data = [];province.forEach((item) => {data.push({name: item.name,value: Math.floor(Math.random() * (1000 - 100 + 1)) + 100,});});return data
};exports.getBarData = () => {const data = []for (let i = 0; i < 7; i++){data.push(Math.floor(Math.random() * (1000 - 100 + 1)) + 100)}return data
}

2、项目入口

也就是app.js,代码如下

const express = require("express");
const WebSocketService = require("./service/websocket_service");const app = express();app.listen(3019, () => {console.log("Server is running on port 3087");
});// 开启服务端监听
// 当客户端连接后,就会监听
WebSocketService.listen()

除了常规启动express服务外,还要引入上面写好的websocket文件,并调用方法监听

后端就这些了

二、前端接收数据并展示

我用vite创建了一个基础的vue3项目,在App.vue组件上写大屏的代码

App.vue

<script setup>
import ChinaMap from '@/components/ChinaMap.vue'
import BarChart from '@/components/BarChart.vue'
</script><template><div class="charts"><div class="chart first"><BarChart /></div><div class="chart second"><ChinaMap /></div><div class="chart third"><BarChart /></div><div class="chart fourth"><BarChart /></div><div class="chart fifth"><BarChart /></div><div class="chart sixth"><BarChart /></div><div class="chart seventh"><BarChart /></div><div class="chart eighth"><BarChart /></div><div class="chart ninth"><BarChart /></div></div>
</template><style lang="scss" scoped>
.charts {height: 100vh;width: 100%;display: grid;gap: 10px;padding: 10px;grid-template-columns: repeat(4, 1fr);grid-template-rows: repeat(3, 1fr);.chart {box-shadow: rgba(6, 24, 44, 0.4) 0px 0px 0px 2px, rgba(6, 24, 44, 0.65) 0px 4px 6px -1px, rgba(255, 255, 255, 0.08) 0px 1px 0px inset;}.first {grid-row: 1/2;grid-column: 1/2;}.second {grid-row: 1/3;grid-column: 2/4;}.third {grid-row: 1/2;grid-column: 4/5;}.fourth {grid-row: 2/3;grid-column: 1/2;}.fifth {grid-row: 2/3;grid-column: 4/5;}.sixth {grid-row: 3/4;grid-column: 1/2;}.seventh {grid-row: 3/4;grid-column: 2/3;}.eighth {grid-row: 3/4;grid-column: 3/4;}.ninth {grid-row: 3/4;grid-column: 4/5;}
}
</style>

使用了grid布局,有个点需要注意一下

写外面包裹的容器时,一定要算好横向和竖向的子容器个数,也就是要写下面两行代码

  grid-template-columns: repeat(4, 1fr);grid-template-rows: repeat(3, 1fr);

表明行3行4列,不写的话,容器的宽高一直会随意变化,grid布局参考我的另一篇博客

根组件中还引入了俩画图的组件,这里我直接上代码了

/components/ChinaMap.vue

<template><div class="chart" ref="chartRef"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from "echarts";
import "@/assets/json/china";
let ws = new WebSocket('ws://localhost:9998');
const chartData = ref([{ name: "山东", value: 2706 },])let interval = null
const handleOpen = () => {console.log('WebSocket connection established');interval = setInterval(sendMessage, 5000)
}const handleClose = () => {console.log('WebSocket connection closed');reLink()
}const handleMessage = (event) => {const { data } = JSON.parse(event.data);chartData.value = datadrawChart()
}const handleError = () => {console.log('WebSocket connection error');
}ws.addEventListener('open', handleOpen)
ws.addEventListener('close', handleClose)
ws.addEventListener('message', handleMessage)
ws.addEventListener('error', handleError)
// 重连
let timer = null
const reLink = () => {console.log('connecting failed, preparing relink...')const relinkTime = 1000timer = setInterval(() => {console.log('relinking....')ws = new WebSocket('ws://localhost:9998')if (ws.readyState === 0) {clearInterval(timer)timer = nullws.addEventListener('open', handleOpen)ws.addEventListener('close', handleClose)ws.addEventListener('message', handleMessage)ws.addEventListener('error', handleError)} else {relinkTime = relinkTime + 5000}}, relinkTime)
}const sendMessage = () => {ws.send('map')
}const chartRef = ref(null)
let initChart
const drawChart = () => {if (initChart != null && initChart != "" && initChart != undefined) {initChart.dispose(); //销毁}initChart = echarts.init(chartRef.value)initChart.setOption({backgroundColor: "transparent", // 设置背景色透明// 必须设置tooltip: {show: false,},visualMap: {text: ["", ""],showLabel: true,left: "100",bottom: "100",min: 0,max: 1000,inRange: {color: ["#edfbfb", "#b7d6f3", "#40a9ed", "#3598c1", "#215096"],},// splitNumber: 5,seriesIndex: "0",},series: [// 地图配置{name: "数值",type: "map",map: "china",zoom: 1,tooltip: {show: true,formatter: function (param) {return param.data.name + ": " + param.data.value},},label: {show: true, // 显示省份名称color: "#000",align: "center",},top: "10%",left: "center",aspectScale: 0.75,roam: true, // 地图缩放和平移itemStyle: {borderColor: "#3ad6ff", // 省分界线颜色  阴影效果的borderWidth: 1,areaColor: "#F5F5F5",opacity: 1,},// 去除选中状态select: {disabled: true,},// 控制鼠标悬浮上去的效果emphasis: {// 聚焦后颜色disabled: false, // 开启高亮label: {align: "center",color: "#ffffff",},itemStyle: {color: "#ffffff",areaColor: "#0075f4", // 阴影效果 鼠标移动上去的颜色},},z: 2,data: chartData.value,},],})
}onMounted(() => {setTimeout(() => {drawChart()}, 200)
});onUnmounted(() => {ws.close();clearInterval(interval)
});
</script><style lang="scss" scoped></style>

画图的代码不解释了,主要是websocket的使用

现在WebSocket已经是window的内置类了,所以不需要安装啥,直接上来生成示例就行了

websocket需要监听open/close/message/error事件,核心的就是message事件了,此外,还做了断开后重连的操作,逻辑是设置定时器重新连接服务端

注意这里的message事件是指接收服务端传递过来的message事件

**有一点需要注意,websocket只能传递字符串类型数据,不能传递对象,**所以后端在使用ws.send时,需要使用JSON.stringify转换格式,前端在接收时也需要使用JSON.parse解码

BarChart.vue中的代码

<template><div class="chart" ref="chartRef"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from "echarts";const chartData = ref([120, 200, 150, 80, 70, 110, 130])let ws = new WebSocket('ws://localhost:9998');
let interval = null
const handleOpen = () => {console.log('WebSocket connection established');interval = setInterval(sendMessage, 5000)
}const handleClose = () => {console.log('WebSocket connection closed');reLink()
}const handleMessage = (event) => {const { data } = JSON.parse(event.data);// console.log(data)chartData.value = datadrawChart()
}const handleError = () => {console.log('WebSocket connection error');
}ws.addEventListener('open', handleOpen)
ws.addEventListener('close', handleClose)
ws.addEventListener('message', handleMessage)
ws.addEventListener('error', handleError)
// 重连
let timer = null
const reLink = () => {console.log('connecting failed, preparing relink...')const relinkTime = 1000timer = setInterval(() => {console.log('relinking....')ws = new WebSocket('ws://localhost:9998')if (ws.readyState === 0) {clearInterval(timer)timer = nullws.addEventListener('open', handleOpen)ws.addEventListener('close', handleClose)ws.addEventListener('message', handleMessage)ws.addEventListener('error', handleError)} else {relinkTime = relinkTime + 5000}}, relinkTime)
}const sendMessage = () => {ws.send('bar')
}const chartRef = ref(null)
let initChart
const drawChart = () => {if (initChart != null && initChart != "" && initChart != undefined) {initChart.dispose(); //销毁}initChart = echarts.init(chartRef.value)initChart.setOption({xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: chartData.value,type: 'bar'}]})
}
onMounted(() => {setTimeout(() => {drawChart()}, 200)
});onUnmounted(() => {ws.close();clearInterval(interval)
});
</script><style lang="scss" scoped></style>

逻辑差不多,不再解释了,最好的做法是把重复的代码放到hooks中去

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2979289.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

autodesk系列软件安装错误1603,手动安装Autodesk Desktop Licensing Service之后,启动服务提示错误1067

一般Autodesk Desktop Licensing Service这个服务没安装或者不正常会导致autodesk系列软件安装错误1603或者其他报错。 手动安装Autodesk Desktop Licensing Service之后&#xff0c;启动服务提示错误1067&#xff0c; 解决方法如下 打开autoremove点击扩展功能&#xff0c;输…

idea上传项目到gitee(码云)

1、打开码云&#xff0c;新建仓库 2、创建 3、这就是创建成功的页面 4、复制仓库地址&#xff0c;后面需要用到 2、打开我们的项目&#xff1a;例如我现在的项目 1、idea创建git仓库 2、选择我们项目文件夹的目录 3、查看文件是否变色&#xff0c;变色表示成功了 4、添加到缓…

广东理工学院携手泰迪智能科技成功部署人工智能实验室

广东理工学院是经国家教育部批准设立的全日制普通本科院校&#xff0c;入选全国应用型人才培养工程培养基地、国家级众创空间试点单位、广东省高校电子商务人才孵化基地。开设34个本科专业&#xff0c;涵盖工学、经济学、管理学、文学、艺术学、教育学等6大学科门类&#xff0c…

智慧水务是什么样的?如何打造智慧水务大屏?

在信息化和数字化快速发展的今天&#xff0c;智慧水务作为城市供水管理的重要组成部分&#xff0c;正变得越来越重要。智慧水务大屏作为智慧水务系统的可视化核心&#xff0c;不仅提升了水务管理的效率&#xff0c;而且通过数据的实时监控和分析&#xff0c;为决策者提供了强有…

代码随想录算法训练营day40

题目&#xff1a;343. 整数拆分、96.不同的二叉搜索树 参考链接&#xff1a;代码随想录 343. 整数拆分 思路&#xff1a;五部曲来走。dp数组&#xff0c;dp[i]用于记录拆i得到的最大乘积和&#xff0c;我们要求的也就是dp[n]&#xff1b;递推公式&#xff0c;我们想拆分i&am…

便携式应急指挥箱规格参数

概况: 微缩型的无线视频音频传输的机动挥所。体积小、重量轻、公配电方便、携带便携、功能齐全。可进行单兵作战&#xff0c;通过此无线音频视频传输的指挥箱能完成现场图像、语音、数据的采集等功能&#xff0c;可以通过5G/4G/WIFI等多种无线网络完成传输的需求&#xff0c;或…

前端性能分析工具及使用

Lighthouse Lighthouse &#xff08;谷歌浏览器的插件商店中搜索并安装&#xff0c;浏览器中点击F12&#xff0c;开发者工具中可使用&#xff09;是 Google 开发的一款工具&#xff0c;用于分析网络应用和网页&#xff0c;收集现代性能指标并提供对开发人员最佳实践的意见。只要…

2024年最新版云开发cms开通步骤,开始开发微信小程序前的准备工作,认真看完奥!

小程序官方有改版了&#xff0c;搞得石头哥不得不紧急的再新出一版&#xff0c;教大家开通最新版的cms网页管理后台 一&#xff0c;技术选型和技术点 1&#xff0c;小程序前端 wxml css JavaScript MINA原生小程序框架 2&#xff0c;数据库 云开发 云数据库 云…

HTTP慢连接攻击的原理和防范措施

随着互联网的快速发展&#xff0c;网络安全问题日益凸显&#xff0c;网络攻击事件频繁发生。其中&#xff0c;HTTP慢速攻击作为一种隐蔽且高效的攻击方式&#xff0c;近年来逐渐出现的越来越多。 为了防范这些网络攻击&#xff0c;我们需要先了解这些攻击情况&#xff0c;这样…

C++判断一个点是否在一个在任意空间中的长方体中

目录 不旋转的长方体计算旋转矩阵及旋转后的长方体计算旋转矩阵计算旋转后的长方体 判断点是否在旋转后的长方体内 不旋转的长方体 如果这个长方体是规则的&#xff0c;不进行任何旋转&#xff0c;可以先计算长方体的BoundingBox&#xff0c;判断点是否在3D的BoundingBox中即可…

软硬件无缝集成:新加坡量子公司首创下一代量子集成平台

为了充分发挥量子系统的性能&#xff0c;已经公认的一个关键方法是在硬件和软件层之间实现紧密集成。这种集成使得软件能够充分利用硬件的所有特性&#xff0c;以实现最佳性能。随着行业努力开发能够提供量子优势的系统&#xff0c;实现这种高性能显然是非常必要的。 Horizon的…

Python学习从0开始——项目一day02数据库连接

Python学习从0开始——项目一day02数据库连接 一、在线云数据库二、测试数据库连接三、数据库驱动介绍四、SQL执行4.1插入测试数据4.2安装数据库连接模块4.3测试SQL语句执行4.4执行SQL的固定步骤及示例 一、在线云数据库 找了一个在线数据库&#xff0c;需要邮箱注册&#xff…

Linux系统中安装MySQL

1、在电脑中安装虚拟机 2、df -h查看光盘是否挂载&#xff0c;没挂载用mount -o ro /dev/sr0 /media命令挂载 3、进入etc/yum.repos.d目录查看仓是否配置&#xff0c;若配置进行下一一步&#xff0c;未配置则进行配置 配置软件仓库 [rootlocalhost yum.repos.d]# vim rhle.r…

tcp inflight 守恒算法背后的哲学

tcp inflight 守恒拥塞控制的正确性 很久以前我开始纠结 tcp 锯齿&#xff0c;很多年后我知道这叫 capacity-seeking&#xff0c;甚至说 tcp 属于 capacity-seeking protocol 的原因就是它早已深入人心的 aimd 行为&#xff0c;而该行为生成了 tcp 锯齿。 在消除锯齿&#xf…

归并排序精讲

一.定义 归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效的排序算法&#xff0c;该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子…

AI交互数字人对教育领域有何优势?

AI交互数字人不仅能够跨越物理距离的限制&#xff0c;以数字人形象为学生提供“面对面”教学互动体验&#xff0c;还能根据学生的具体需求提供个性化的知识解答。如天津大学推出了数字人老师&#xff0c;以刘艳丽教授形象1&#xff1a;1仿真打造的2.5D数字人&#xff0c;能够应…

【深度学习】行人跌倒行为检测软件系统

行人跌倒检测系统在各个领域的应用都对社会的整体健康、安全和福祉产生积极影响&#xff0c;为人们的生活和工作提供了更加安全和可靠的环境&#xff0c; 本文主要使用YOLOV8深度学习框架自训练了一个“行人跌倒检测模型”&#xff0c;基于此模型使用PYQT5实现了一款界面软件用…

MATLAB实现蚁群算法栅格路径优化

蚁群算法是一种模拟自然界中蚂蚁觅食行为的优化算法&#xff0c;常用于解决路径规划问题。在栅格路径优化中&#xff0c;蚁群算法可以帮助找到从起点到终点的最优路径。以下是蚁群算法栅格路径优化的基本流程步骤&#xff1a; 初始化参数&#xff1a; (1)设置蚂蚁数量&#xff…

Rest微服务案例

Rest 父工程构建microservicecloud-api公共子模块Modulemicroservicecloud-provider-dept-8001部门微服务提供者Modulemicroservicecloud-consumer-dept-80部门微服务消费者Module 以Dept部门模块做一个微服务通用案例 Consumer消费者&#xff08;Client&#xff09;通过REST调…

Go 堆内存分配源码解读

简要介绍 在Go的内存分配中存在几个关键结构&#xff0c;分别是page、mspan、mcache、mcentral、mheap&#xff0c;其中mheap中又包括heapArena&#xff0c;具体这些结构在内存分配中担任什么角色呢&#xff1f; 如下图&#xff0c;可以先看一下整体的结构&#xff1a; mcach…