Skip to content

05. 前端集成示例 (UniApp & JS)

1. UniApp 全局封装示例 (utils/socket.js)

javascript
export default class UniSocket {
    constructor(url, token) {
        this.url = `${url}/ws?token=${token}`;
        this.socketTask = null;
        this.isOpen = false;
        this.reconnectCount = 0;
    }

    connect() {
        this.socketTask = uni.connectSocket({ url: this.url });
        this.socketTask.onOpen(() => {
            this.isOpen = true;
            this.reconnectCount = 0;
            this.startHeartbeat();
        });
        this.socketTask.onMessage((res) => {
            const data = JSON.parse(res.data);
            uni.$emit('socketMessage', data);
        });
        this.socketTask.onClose(() => {
            this.isOpen = false;
            this.attemptReconnect();
        });
    }

    send(data) {
        if (this.isOpen) {
            this.socketTask.send({ data: JSON.stringify(data) });
        }
    }

    startHeartbeat() {
        this.timer = setInterval(() => this.send({ cmd: 0, data: "ping" }), 30000);
    }

    attemptReconnect() {
        if (this.reconnectCount < 5) {
            setTimeout(() => {
                this.reconnectCount++;
                this.connect();
            }, 5000);
        }
    }
}

2. Vue 3 (Composition API) 完整示例

在 Vue 3 中,推荐使用 reactive 维护消息列表,并利用 onMountedonUnmounted 管理生命周期。

2.1 封装核心 Hook (hooks/useSocket.js)

javascript
import { ref, onMounted, onUnmounted } from 'vue';

export function useSocket(url, token) {
    const socket = ref(null);
    const isConnected = ref(false);
    const messages = ref([]);

    const connect = () => {
        socket.value = new WebSocket(`${url}/ws?token=${token}`);

        socket.value.onopen = () => {
            isConnected.value = true;
            console.log('✅ WebSocket Connected');
        };

        socket.value.onmessage = (event) => {
            const data = JSON.parse(event.data);
            messages.value.push(data);
        };

        socket.value.onclose = () => {
            isConnected.value = false;
            // 可以在这里处理自动重连
        };
    };

    const sendMessage = (receiverId, content) => {
        if (socket.value && isConnected.value) {
            socket.value.send(JSON.stringify({
                cmd: "send_private_msg",
                data: { receiverId, content, contentType: 'text' }
            }));
        }
    };

    onMounted(() => connect());
    onUnmounted(() => socket.value?.close());

    return { messages, isConnected, sendMessage };
}

2.2 页面组件使用 (ChatComponent.vue)

vue
<script setup>
import { ref } from 'vue';
import { useSocket } from '@/hooks/useSocket';

const { messages, isConnected, sendMessage } = useSocket('ws://localhost:8060', 'your_token');
const inputText = ref('');

const handleSend = () => {
    if (!inputText.value) return;
    sendMessage('456', inputText.value); // 发送给用户 456
    inputText.value = '';
};
</script>

<template>
  <div class="chat-container">
    <div class="status">状态: {{ isConnected ? '在线' : '离线' }}</div>
    <div class="message-list">
      <div v-for="(msg, index) in messages" :key="index" class="message-item">
        <span class="sender">{{ msg.senderId }}:</span>
        <span class="content">{{ msg.data?.content || msg.content }}</span>
      </div>
    </div>
    <div class="input-box">
      <input v-model="inputText" @keyup.enter="handleSend" placeholder="输入消息..." />
      <button @click="handleSend">发送</button>
    </div>
  </div>
</template>

3. UniApp 页面使用示例 (Vue 3 风格)

javascript
onLoad() {
    // 监听消息
    uni.$on('socketMessage', (msg) => {
        if (msg.contentType === 'text') {
            this.msgList.push(msg.data);
        }
    });
}

methods: {
    handleSend() {
        this.$socket.send({
            cmd: "send_private_msg",
            data: { receiverId: '456', content: 'Hello', contentType: 'text' }
        });
    }
}

Released under the MIT License.