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://www.mzph.cn/pingmian/2181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多项式和Bezier曲线拟合

目录 1. 多项式拟合2. Bezier曲线拟合3. 源码地址 1. 多项式拟合 在曲线拟合中&#xff0c;多项式拟合方法的性能受到三个主要因素的影响&#xff1a;采样点个数、多项式阶数和正则项。 采样点个数 N N N&#xff1a;从Figure 1中可以看出较少的采样点个数可能导致过拟合&…

2024统计建模:大数据与人工智能时代的统计研究

文章目录 题目解读你需要具备的知识点课题推荐视频分析 题目解读 主要做的是“大数据”与“人工智能”。 其中“大数据”所涉及的的第一个就是大量的数据&#xff0c;数据从哪里来&#xff1f;拿到数据后&#xff0c;我们需要做基本的数据分析&#xff0c;如何对大量的数据进…

Springboot2.7解决静态资源302问题

application.yml配置&#xff1a; spring: mvc: static-path-pattern: /static/** web: resources: static-locations: classpath:/static/ 配置后&#xff0c;静态资源是生效了。 但在页面上显示有些是200&#xff0c;有些是302&#xff0c;不同的状态&#xff0c; 觉得很…

华为OD机试真题-贪吃的猴子-2024年OD统一考试(C卷D卷)

题目描述: 一只贪吃的猴子,来到一个果园,发现许多串香蕉排成一行,每串香蕉上有若干根香蕉。每串香蕉的根数由数组numbers给出。猴子获取香蕉,每次都只能从行的开头或者末尾获取,并且只能获取N次,求猴子最多能获取多少根香蕉。 输入描述: 第一行为数组numbers的长度 第二…

Flink cdc如何只进行增量同步,不同步历史数据(只读取binlog)

环境 flink: 1.15.3 flink-connector-mysql-cdc: 2.4.0 场景&#xff1a; mysql cdc到starrocks&#xff0c; 目前线上环境以及有老的任务在同步&#xff0c;现在升级了任务&#xff08;旧checkpoints无法使用&#xff09;旧表里面数据特别大&#xff0c;不方便重新同步 方…

Window系统mysql数据库定时备份脚本

将下面的内容复制到bat文件里&#xff0c;然后将5个变量进行修改&#xff1a; mysqldumpPath 你本地mysql路径 dbName 需要备份的数据库 dbUser 数据库用户名 dbPwd 数据库密码 outpurDir 备份文件输出路径 echo 取日期、时间变量值 set yy%date:~0,4% set mm%date:~5,2% set …

Java8 Optional类使用总结

前言 相信不少小伙伴已经被java的NPE(NullPointer Exception)所谓的空指针异常搞的头昏脑涨, 有大佬说过“防止 NPE&#xff0c;是程序员的基本修养。”但是修养归修养&#xff0c;也是我们程序员最头疼的问题之一&#xff0c;那么我们今天就要尽可能的利用Java8的新特性 Opti…

聚道云一键打通金蝶宁波银行,财务效率暴涨10倍!

客户介绍&#xff1a; 某农资有限公司是一家集农资贸易、仓储物流、农机服务为一体的大型企业。随着业务规模的不断扩大&#xff0c;传统的手动财务操作模式已难以满足其需求。公司急需寻找一种方法&#xff0c;将金蝶财务软件与宁波银行对接&#xff0c;实现资金流转自动化和…

深入React Hooks:从源码剖析到高级实践(Vue 同学必看指南)

深入React Hooks&#xff1a;从源码剖析到高级实践 React Hooks作为一项重大革新&#xff0c;彻底改变了React应用的状态管理与生命周期逻辑&#xff0c;极大地提升了代码的可读性、可复用性和简洁性。本文将带领熟悉Vue技术栈的读者&#xff0c;浅析React Hooks的内在机制&am…

python使用selenium如何获取一个div下所有的文本

在Python中使用Selenium获取一个<div>元素下所有的文本可以通过多种方式实现。一种简单的方法是首先获取<div>元素&#xff0c;然后使用text属性来获取其内部的文本内容。但这种方法可能不会获取到<div>元素内嵌套的其他标签&#xff08;如<span>、<…

045、seq2seq

之——序列生成 杂谈 基于RNN实现&#xff0c;通过RNN生成器不断获取输入&#xff0c;更新隐藏状态&#xff0c;将最后生成的隐藏状态传递给解码器&#xff0c;然后自循环迭代直到输出停止。 正文 1.训练 训练时候解码器使用目标句子不断作为输入&#xff0c;就算解码错了输入…

C# 通过阿里云 API 实现企业工商数据查询

目录 应用场景 关于阿里云企业工商数据查询API 开发运行环境 类设计 类属性 类方法 实现代码 创建 Company 类 调用举例 小结 应用场景 在企业会员后台注册系统中&#xff0c;为验证企业名称是否输入完整且是有效存在的&#xff0c;则可以通过云API服务的方式进行验…

如何解决升级IntelliJ IDEA 2024后 打开项目就自动闪退关闭问题的终极指南

title: “&#x1f42f; 解决升级IntelliJ IDEA 2024后项目自动关闭的终极指南” date: 2024-04-23 author: 猫头虎 profile: CSDN 文章目录 title: "&#x1f42f; 解决升级IntelliJ IDEA 2024后项目自动关闭的终极指南" date: 2024-04-23 author: 猫头虎 profile: …

​「Python绘图」绘制皮卡丘

python 绘制皮卡丘 一、预期结果 二、核心代码 import turtle print("开始绘制皮卡丘") def getPosition(x, y):turtle.setx(x)turtle.sety(y)print(x, y)class Pikachu:def __init__(self):self.t turtle.Turtle()t self.tt.pensize(3)t.speed(190)t.ondrag(getP…

GC垃圾回收

垃圾回收 1、什么是 垃圾回收机制&#xff1a; 理解Java的垃圾回收机制&#xff0c;就要从&#xff1a;“什么时候”&#xff0c;“对什么东西”&#xff0c;“做了什么”三个方面来具体分析。 ​ 第一&#xff1a;“什么时候”即就是GC触发的条件。 ​ GC触发的条件有两种…

喜报 | 一致认可!擎创科技连续6年获“鑫智奖”专家推荐TOP10优秀解决方案

为展示金融企业数据管理和数据平台智能化转型成果&#xff0c;分享大数据和人工智能在风控、营销、产品、运营等场景的落地实践&#xff0c;探讨“金融科技数据智能”的创新应用和未来发展&#xff0c;在全球金融专业人士协会的支持下&#xff0c;金科创新社主办了“鑫智奖第六…

uniapp微信小程序(商城项目)

最近&#xff0c;闲来无事&#xff0c;打算学一下uniapp小程序 于是在跟着某站上学着做了一个小程序&#xff0c;主要是为了学uniapp和vue。某站黑马优购 完成的功能主要有&#xff1a;首页、搜索、分类和购物车。 有人问了为什么没有登录、和添加订单呢&#xff1f;问的很好…

【力扣 Hot100 | 第五天】4.20(回文链表)

1.回文链表 1.1题目 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例一&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例二&#xff1a; 输入…

vue实现水平排列且水平居中

样式实现 .body{text-align: center; } .body_content{display: inline-block; } .body_content_cardList{display: flex;flex-wrap: wrap;text-align: center; }<div class"body"><div class"body_content"><div class"body_content…

ESP32学习第一天-ESP32点亮LED,按键控制LED状态,LED流水灯

第一天使用到的函数: 函数第一个参数设置哪一个引脚&#xff0c;第二个参数设置引脚模式。 pinMode(led_pin,OUTPUT); //设置引脚模式 函数的第一个参数设置哪一个引脚&#xff0c;第二个参数设置是高电平还是低电平。 digitalWrite(led_pin,HIGH);//将引脚电平拉高 #incl…