Skip to content
当前页

WebSocket

使用@vueuse/core 中的 useWebSocket

使用说明

  • 使用
ts
import { useWebSocket } from '@vueuse/core'

const { status, data, send, open, close } = useWebSocket('ws://websocketurl')
  • 自动连接:(默认情况下启用),也可以使用open() 手动启动

  • 自动关闭连接: 根据页面的生命周期,自动关闭连接,也可以使用close()手动关闭

  • 自动进行异常重试:

    • autoReconnect: true,
    • autoReconnect: 如下:自动重试3次,每次延迟1秒,失败后执行onFailed()
    {
      retries: 3,
      delay: 1000,
      onFailed() {
        alert('Failed to connect WebSocket after 3 retires')
      },
    },
  • 心跳测试连接

    • heartbeat: true,
    • heartbeat: 如下,心跳时发送消息
      {
        message: 'ping',
        interval: 1000,
      }
  • 子协议:protocols: ['soap'], // ['soap', 'wamp']

示例

  • 正常发送消息 yes.png
  • 断开连接后尝试重试3次 error.png

示例代码

示例位置:src/views/demo/pages/comp/websocket/index.vue

vue
<template>
  <div class="flex">
    <div class="w-1/3 bg-white p-4">
      <div class="flex items-center">
        <span class="text-lg font-medium mr-4"> 连接状态: </span>
        <el-tag :type="getTagColor">{{ status }}</el-tag>
      </div>
      <hr class="my-4" />

      <div class="flex">
        <el-input v-model:value="state.server" disabled>
          <template #addonBefore> 服务地址 </template>
        </el-input>
        <el-button :type="getIsOpen ? 'danger' : 'primary'" @click="toggle">
          {{ getIsOpen ? '关闭连接' : '开启连接' }}
        </el-button>
      </div>
      <p class="text-lg font-medium mt-4">设置</p>
      <hr class="my-4" />
      <el-input
        v-model="state.sendValue"
        :rows="2"
        :disabled="!getIsOpen"
        placeholder="需要发送到服务器的内容"
        type="textarea"
      />

      <el-button
        type="primary"
        block
        class="mt-4"
        :disabled="!getIsOpen"
        @click="handlerSend"
      >
        发送
      </el-button>
    </div>

    <div class="w-2/3 bg-white ml-4 p-4">
      <span class="text-lg font-medium mr-4"> 消息记录: </span>
      <hr class="my-4" />

      <div class="max-h-80 overflow-auto">
        <ul>
          <li v-for="item in getList" class="mt-2" :key="item.time">
            <div class="flex items-center">
              <span class="mr-2 text-primary font-medium">收到消息:</span>
              <span>{{ formatToDateTime(item.time) }}</span>
            </div>
            <div>
              {{ item.result }}
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { useWebSocket } from '@vueuse/core';
  import { computed, reactive, watchEffect } from 'vue';
  import { formatToDateTime } from '/@/utils/dateUtil';

  const state = reactive({
    server: 'ws://localhost:8000',
    sendValue: '',
    recordList: [] as { id: number; time: number; res: string }[],
  });
  const { status, data, send, close, open } = useWebSocket(state.server, {
    autoReconnect: {
      retries: 3,
      delay: 1000,
      onFailed() {
        alert('Failed to connect WebSocket after 3 retires');
      },
    },
    // heartbeat: {
    //   message: 'ping',
    //   interval: 1000,
    // },
  });
  watchEffect(() => {
    console.log(state);
    if (data.value) {
      try {
        const res = JSON.parse(data.value);
        state.recordList.push(res);
      } catch (error) {
        state.recordList.push({
          res: data.value,
          id: Math.ceil(Math.random() * 1000),
          time: new Date().getTime(),
        });
      }
    }
  });

  const getIsOpen = computed(() => status.value === 'OPEN');
  const getTagColor = computed(() => (getIsOpen.value ? 'success' : 'danger'));

  const getList = computed(() => {
    return [...state.recordList].reverse();
  });

  function handlerSend() {
    send(state.sendValue);
    state.sendValue = '';
  }

  function toggle() {
    if (getIsOpen.value) {
      close();
    } else {
      open();
    }
  }
</script>

<style scoped></style>