springboot + vue + websocket + ECharts
初衷
因为刚接触VUE的前端开发,自己也是做起来也是模模糊糊的。遇到的一些坑记录一下
缘由
在完成一个页面展示的折线图时,虽然每次刷新都是直接从后端直接拿最新的数据,但是会导致界面的整体刷新,就想做一个异步的刷新,让折线图能像股市看板一样的存在,线是能实时变化的。思考了一下有两种解决方案
- 在前端JS中设置一个定时器,每秒往后端请求一次(这种感觉太耗资源了,尤其有时候需要看日志,控制台打印出来的东西根本看不了,所以直接放弃了这种)
- 使用websocket通讯,保证一个长连接,后端主动像前端推送消息(从被动的被访问变成主动,当需要时才给前端数据)
后端Springboot实现
1.创建一个配置文件完成websocket的启动
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
*
* @author 解咚咚
* @date 创建时间2019年6月25日上午10:04:54
* @version 1.0
*/
@Configuration
public class myWebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
2.之后可以完成和前端建立连接的一些功能了
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.fh.entity.PageData;
import org.java_websocket.server.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.chaintWMS.mapper.HomePageMapper;
import com.chaintWMS.utils.Result;
/**
*
* @author 解咚咚
* @date 创建时间2019年6月25日上午10:26:59
* @version 1.0
*/
@ServerEndpoint(value = "/websocket") //这里是前端访问地址时需要带的。
@Component
public class MyWebsocket {
private static HomePageMapper homePageMapper;
// 注入的时候,给类的 service 注入
@Autowired
public void setChatService(HomePageMapper homePageMapper) {
MyWebsocket.homePageMapper = homePageMapper;
}
//上面这么写的原因是因为直接用 @Autowired注入会导致 homePageMapper未null 注入不成功,所以需要这么去写。具体原因可去百度其他文档
//其实这里还可以 写一个连接数的静态变量,当每次连接成功后 ++一次,断开连接时 -- 一次,可以实时观测连接数,根据具体需求去做就可以了
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<MyWebsocket> webSocketSet = new CopyOnWriteArraySet<MyWebsocket>();
//private volatile static List<Session> sessions = Collections.synchronizedList(new ArrayList());
private Session session;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //将连接添加到安全的线池去
System.out.println("连接成功");
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
System.out.println("有一连接关闭");
}
/**
* 给前台推消息
* @param session
* @param rs
* @throws IOException
*/
public void sendMessage(String rs) throws IOException {
if(this.session.isOpen()) { //判断session是否是打开状态的,打开状态的才发。
this.session.getBasicRemote().sendText(rs);
}
}
//下面这个方法是对外提供的静态方法,当业务层需要给改变前端显示内容,或给前端推送内容时调用此方法(可根据具体业务逻辑更改此方法)
public static void sendInfo(String message) throws IOException {
List<PageData> hhlist = homePageMapper.gethhData();
String json = Result.of(200,"得到实时数据成功").put("hhlist", hhlist).json();
if(message.equals("1")) {
for (MyWebsocket item : webSocketSet) { //这是默认给全部的连接发送消息 如果不需要给全部发,可以定义一个变量来区别每次链接的ID 给对应的ID发
item.sendMessage(json);
}
}
}
/**
* 收到前端客户端消息后调用的方方法
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
}
/**
* 发生异常时调用
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生异常");
error.printStackTrace();
}
}
前端VUE的实现
1.在 methods里创建一个初始化websocket的函数
initWebSocket(){ //初始化weosocket
const $vm = this
let ws = new WebSocket('ws://10.113.5.246:8080/chaintWMS/websocket'); //这里new 一个WebSocket对象将连接地址传入(这个连接地址很重要,很多情况连接不上就是连接地址没用写对)
ws.onopen = () => {
// Web Socket 已连接上,使用 send() 方法发送数据
console.log('发送数据中。。。')
ws.send('你好')
console.log('数据发送完成。')
}
ws.onmessage = evt => { //后端发来的消息接收(注意数据放在evt的data中并且是个JSON字符串,需要parse成JSON对象去使用)
let hhlist = JSON.parse(evt.data);
//具体业务逻辑
$vm.$set($vm.panelData,"rsumhh",psu) //这里是 vue给一些已经改变了的图表数据后,显示内容没用变化时用的。(直接用=去赋值界面的显示是不会变的 Echarts 图表就是需要用 set去改变值的变化)
console.log('数据已接收...')
}
ws.onclose = function () {
// 关闭 websocket
console.log('连接已关闭...')
}
// 路由跳转时结束websocket链接
this.$router.afterEach(function () {
ws.close()
})
}
2.然后在created()中添加方法的调用
created() {
//页面刚进入时开启长连接
this.initWebSocket()
},
Echart动态拿数据时不显示原因(加载顺序问题)
1.需要在后端拿数据时,进行异步时,需要在拿完数据之后 在调用 this.initChart() 初始化的方法(在去加载图表信息)
getWarehouseInventory() {
const $vm = this
getWarehouseInventory().then(res => {
if (res.code === 200) {
for (var i in res.warehouseinventory) {
$vm.xvalue.push(res.warehouseinventory[i].WarehouseCode)
// $vm.wnvalue.push(res.warehouseinventory[i].netweight)
$vm.wavalue.push(res.warehouseinventory[i].Amount)
}
this.initChart() //ECharts的具体实现方法
}
})
}
结语
第一次写,感觉很多没用表达清楚,也就记录一下自己在开发中遇到的一些坑吧。这此的坑主要就是 VUE的赋值机制问题,直接使用 =等于去赋值并不会改变显示的值,需要用 set 去改变,还有当Echart加载时顺序的问题