vue+websocket+Stomp组件实现前端长连接

1文件结构

请添加图片描述

2.重点文件夹中的文件代码以及作用

① 根目录中systemConfig文件夹中的main.js文件

/**
 *作用:作为项目中的配置文件,放了websocket连接的url,用户名(admin),密码(123456)
 *项目中基础API等一些配置参数
 * ==================================
 */
/* eslint-disable */
(function (global, factory) {
  "use strict";

  if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = global.document ?
      factory(global) :
      function (w) {
        if (!w.document) {
          throw new Error('systemConfig must apply to a window with document');
        }
        return factory(w)
      }
  } else {
    factory(global)
  }

})(typeof window !== 'undefined' ? window : this, function (window) {
  "use strict";
  const systemConfig = window.systemConfig = {
    /**
     *
     * 该配置项中的路径配置会覆盖系统内部配置项,主要用于部署应用时,地址变动等情况。
     * =======================================================================
     * 请严格按照配置相修改,禁止添加任何其他内容至该文件中
     *
     */
    api: {
      // 开发地址配置
      mockBaseUrl: '',
      // 生成部署地址配置
      prodBaseUrl: '',
    },
    websocket: [{	// 下面的三个参数是需要和后端沟通的
      url: '',
      login: '',
      passcode: '',
      name: ''
    }],
  }
  return systemConfig
})

②根目录中的src下的config中的externalConfigDetect下的index.js文件

/**作用:对配置参数安全校验
 * ==============================================
 * Author: wang
 * Date: 2022.3.4
 *
 */
import systemConfig from 'systemConfig'

class PlatformBaseConfig {
  constructor () {
    this.config = systemConfig
  }

  getConfig () {
    if (detectCurrectConfig(this.config)) {
      const { api, websocket } = this.config
      return {
        api,
        websocket,
        external: true
      }
    } else {
      console.error('外部配置文件引入失败,系统将采用内置原始配置。如需正常引用,请先检查外部配置项。')
      return {
        external: false
      }
    }
  }

  getApi () {
    return this.getConfig().api || {}
  }

  getWs () {
    return this.getConfig().websocket || []
  }
}

// 对外部引入的对象做一些安全检查
function detectCurrectConfig (config) {
  if (typeof (config) !== 'object' || typeof (config) === 'undefined') return false
  if (!config.api || !config.websocket) return false
  const apiKeys = Object.keys(config.api)
  if (apiKeys.indexOf('mockBaseUrl') === -1 || apiKeys.indexOf('prodBaseUrl') === -1) {
    return false
  }
  config.websocket.map((item) => {
    const wsKeys = Object.keys(item)
    if (wsKeys.indexOf('url') === -1 || wsKeys.indexOf('login') === -1 || wsKeys.indexOf('passcode') === -1) {
      return false
    }
  })
  return true
}

export default new PlatformBaseConfig()

③根目录中的src下的config中的externalConfigDetect下的settings.js文件

/**作用:统一输出配置参数
 * Global Configurations
 * =========================
 * Author: wang
 * Date: 2022.3.4
 */

// 此文件只需要看Websocket 配置项即可
import platformBaseConfig from './externalConfigDetect'

/**
  * API接口默认参数配置
*/
const baseConfig = {
  isMocked: process.env.NODE_ENV !== 'production',
  isDebug: process.env.NODE_ENV !== 'production',
  sep: '.'
}
export const API_DEFAULT_CONFIG = Object.assign(baseConfig, platformBaseConfig.getApi())

/**
 * Websocket 配置项
 */
const baseWsConfig = []
export const WS_CONFIG = Object.assign(baseWsConfig, platformBaseConfig.getWs())

④根目录中的src下的config中的plugins下的websocket.js文件

/**
 * ================================
 * Content: 使用class创建websocket
 * Author: wang
 * Date: 2022.3.4
 * Technology: Stomp组件 WebSocket
 * ================================
 */

// Stomp组件
import Stomp from 'stompjs'
import { WS_CONFIG } from '@/config/settings'
// import store from '@/plugins/store'
class DispatchWebsocket {
  // constructor 方法是类的构造函数,是一个默认方法,通过 new 命令创建对象实例时,自动调用该方法
  constructor ({ url, login, passcode }) {
    this.url = url
    this.ws = null
    this.client = null
    this.headers = {
      login: login,
      passcode: passcode
    }
    this.onConnect = {}
    this.onError = () => {}
    this.isConnect = false
    this.count = 0
    this.timer = null
  }

  createWSConnection (ws) {
    if (!ws && this.url) {
      return new Promise((resolve, reject) => {
        this.ws = new WebSocket(this.url)
        this.client = Stomp.over(this.ws)
        this.client.debug = false
        this.onConnect = () => {
          this.isConnect = true
          resolve()
        }
        this.onError = () => {
          this.reconnectWSConnection()
          reject(new Error('创建websoket链接失败.'))
        }
        this.client.connect(this.headers, this.onConnect, this.onError)
      })
    } else {
      console.warn('已经创建了webscoket链接,不需要重复创建!')
    }
  }

  reconnectWSConnection () {
    this.isConnect = false
    if (this.timer === null) {
      this.timer = setInterval(() => {
        if (this.count === 150) {
          console.log('ws重连5分钟未成功,请刷新页面')
          clearInterval(this.timer)
          return
        }
        this.ws = new WebSocket(this.url)
        this.client = Stomp.over(this.ws)
        this.client.debug = false
        this.handleSuccess = () => {
          console.log('重新连接成功!')
          this.count = 0
          this.isConnect = true
          clearInterval(this.timer)
          this.timer = null
        }
        this.handleError = () => {
          console.error('重新连接失败!')
          const reconInv = setInterval(() => {
            clearInterval(reconInv)
            this.reconnectWSConnection()
          }, 10000)
        }
        this.client.connect(this.headers, this.handleSuccess, this.handleError)
        this.count++
      }, 2000)
    }
  }

  destroyWSConnection () {
    return new Promise((resolve) => {
      this.ws && this.client.disconnect(() => {
        this.isConnect = false
        resolve()
      })
    })
  }
}

export default {
  install (Vue) {
    WS_CONFIG.forEach((item) => {
      console.log('item', item)
      const ws = new DispatchWebsocket(item)
      ws.createWSConnection(Vue.prototype[`$ws${item.name}`])
      // 消息提示推送
      Object.defineProperty(Vue.prototype, `$ws${item.name}`, {
        value: ws
      })
    })
    // ws.createWSConnection()
    // Vue.prototype.$ws = websocket
    // 绑定websocket至原型对象
  }
}

⑤以上四步就是简单的创建websocket,这一步是如何将websocket挂载到vue项目中
在根目录的main.js文件中

import Vue from 'vue'
import App from '@/App.vue'
import router from '@/router'
import store from '@/store'
import ElementUI from 'element-ui'
import websocket from '@/plugins/websocket'

import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)
// 此处创建websocket在性能上还可以优化,项目中建议将websocket创建的时机放在用户登录成功之后
Vue.use(websocket)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

⑥使用

<template>
  <div>
    <el-button type="success" round @click="handleOpen">开启ws</el-button>
    <el-button type="success" round @click="handleClose">关闭ws</el-button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      lineIdArray: ["6489", "63699"],
      lineSubscibes: [],
    };
  },
  mounted() {},
  methods: {
    handleOpen() {
      // JavaScript 类不是对象。它是 JavaScript 对象的模板。
      this.lineIdArray.forEach((lineId) => {
        this.subcribeLine(lineId);
      });
    },
    handleClose() {
      this.destroyWSConnection();
    },
    subcribeLine(lineId) {
      this.lineSubscibes[lineId] = this.$ws.client.subscribe(
        `/topic/pos.base.${lineId}.*`,
        (data) => {
          const busData = JSON.parse(data.body);
          console.log(busData, busData);
        }
      );
    },
    destroyWSConnection() {
      console.log(this.lineSubscibes);
      if (this.lineIdArray.length > 0) {
        console.log("-------------------------------------------");
        for (let index = 0; index < this.lineIdArray.length; index++) {
          let unscr = this.lineSubscibes[this.lineIdArray[index]];
          console.log("unscr", unscr);
          if (unscr) {
            console.log(`执行了${unscr}`);
            unscr.unsubscribe();
          }
        }
      }
    },
  },
};
</script>

4.按照我提供的文件路劲和代码就能够创建出一个简单的websocket的demo,注意需要后端配合

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的使用Spring Boot、Vue.jsWebSocket实现的聊天室的代码示例: Spring Boot后端代码: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(), "/chat").setAllowedOrigins("*"); } @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); } } class WebSocketHandler extends TextWebSocketHandler { private static final Map<WebSocketSession, String> users = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) { users.put(session, "Anonymous"); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { ChatMessage chatMessage = new ObjectMapper().readValue(message.getPayload(), ChatMessage.class); if (chatMessage.getType() == ChatMessage.MessageType.JOIN) { users.put(session, chatMessage.getSender()); } for (WebSocketSession user : users.keySet()) { user.sendMessage(new TextMessage(new ObjectMapper().writeValueAsString(chatMessage))); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { users.remove(session); } } @Data @AllArgsConstructor @NoArgsConstructor class ChatMessage { public enum MessageType { CHAT, JOIN, LEAVE } private String sender; private String content; private MessageType type; public static ChatMessage joinMessage(String sender) { return new ChatMessage(sender, "", MessageType.JOIN); } public static ChatMessage leaveMessage(String sender) { return new ChatMessage(sender, "", MessageType.LEAVE); } } @RestController public class ChatController { @GetMapping("/users") public List<String> users() { return new ArrayList<>(WebSocketHandler.users.values()); } } ``` Vue.js前端代码: ```html <template> <div> <h2>Chat Room</h2> <div> <label>Your name:</label> <input v-model="name" @keyup.enter="join" /> <button @click="join">Join</button> </div> <div v-if="joined"> <div> <label>Message:</label> <input v-model="message" @keyup.enter="send" /> <button @click="send">Send</button> </div> <div> <h3>Users:</h3> <ul> <li v-for="user in users" :key="user">{{ user }}</li> </ul> </div> <div> <h3>Chat:</h3> <ul> <li v-for="chat in chats" :key="chat.id"> <strong>{{ chat.sender }}:</strong> {{ chat.content }} </li> </ul> </div> </div> </div> </template> <script> import SockJS from "sockjs-client"; import Stomp from "stompjs"; export default { data() { return { name: "", message: "", joined: false, chats: [], users: [], stompClient: null, }; }, methods: { join() { const socket = new SockJS("/chat"); this.stompClient = Stomp.over(socket); this.stompClient.connect({}, () => { this.stompClient.subscribe("/topic/chat", (message) => { const chat = JSON.parse(message.body); if (chat.type === "JOIN") { this.users.push(chat.sender); } else if (chat.type === "LEAVE") { this.users.splice(this.users.indexOf(chat.sender), 1); } this.chats.push(chat); }); this.stompClient.send( "/app/chat", JSON.stringify(ChatMessage.joinMessage(this.name)) ); this.joined = true; }); }, send() { this.stompClient.send( "/app/chat", JSON.stringify( new ChatMessage(this.name, this.message, ChatMessage.MessageType.CHAT) ) ); this.message = ""; }, }, }; class ChatMessage { static MessageType = { CHAT: "CHAT", JOIN: "JOIN", LEAVE: "LEAVE", }; constructor(sender, content, type) { this.sender = sender; this.content = content; this.type = type; } static joinMessage(sender) { return new ChatMessage(sender, "", ChatMessage.MessageType.JOIN); } static leaveMessage(sender) { return new ChatMessage(sender, "", ChatMessage.MessageType.LEAVE); } } </script> ``` 在这个示例中,我们使用了Spring Boot的WebSocket支持来处理来自客户端的事件。我们创建了一个WebSocket处理程序,它维护了一个用户会话列表,并在用户加入、离开或发送聊天消息时广播消息到所有连接的客户端。我们还为WebSocket处理程序创建了一个控制器,以便在客户端请求所有当前连接的用户列表时返回它们。 在Vue.js应用程序中,我们使用SockJS和Stomp.js来建立与服务器的WebSocket连接,并处理来自服务器的事件。我们使用Vue.js的数据绑定来更新聊天消息、用户列表和用户输入框中的数据,并在加入聊天室、发送聊天消息或断开连接时发送相关的WebSocket事件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值