随着移动互联网的发展,即时通讯应用变得越来越普遍。本文将介绍如何使用uni-app框架结合WebSocket实现一个简单的实时聊天功能。
准备工作
- 确保已经安装了uni-app开发环境。
- 了解基本的Vue.js知识。
- WebSocket服务器已经搭建好并运行正常。
创建项目
- 使用HBuilderX创建一个新的uni-app项目。
- 在项目中添加必要的组件和样式。
WebSocket 混入模块
首先,我们需要创建一个混入模块来管理WebSocket连接的状态。这个模块将被引入到聊天界面中。
创建 WebSocket 混入模块
在项目的mixins
目录下创建一个名为socket.js
的文件,内容如下:
export const socket = {
data() {
return {
// socket是否开启
socketOpen: false,
// 定时器
timer: null,
// 链接
surl: `ws://120.55.76.143:48080/infra/ws?token=66971089117e4372847ff4f31bc538df`, // 请替换为实际的WebSocket服务器URL
// 底部id用于定位到底部
scrollIntoView: "",
// 键盘高度
keyboardHeight: 0,
// 监听键盘高度的方法
listener: null
}
},
onLoad(option) {
// 开启键盘高度监听
this.listenerKeyboardHeight()
// socket初始化
this.init()
// 定时器,定时判断socket有没有掉线
this.timer = setInterval(() => {
this.isSocketConnct()
}, 2000)
},
beforeDestroy() {
// 关闭定时器
clearInterval(this.timer)
// 关闭键盘高度监听
uni.offKeyboardHeightChange(this.listener)
// 关闭Socket
this.closeSocket()
},
methods: {
// 发送消息
sendSocketMessage(msg) {
console.log("发送消息", msg);
let data = {
content: {
"fromUserId": uni.getStorageSync("userId"),
"text": msg,
"single": false,
},
type: "member-message"
}
data = JSON.stringify(data)
let that = this
if (this.socketOpen) {
uni.sendSocketMessage({
data,
success: (res) => {
setTimeout(() => {
// json转对象
let param = JSON.parse(data)
that.sendMessageHandle(param)
}, 300)
},
fail(err) {
// 发送失败处理
}
});
} else {
// Socket没有开启,重新连接并重新发送消息
this.init()
setTimeout(() => {
this.sendSocketMessage(msg)
}, 300)
}
},
// 判断是否连接
isSocketConnct() {
if (!this.socketOpen) {
console.log("WebSocket 再次连接!");
this.init()
}
},
// 初始化
init() {
this.connect()
this.openSocket()
this.onclose()
this.onSocketMessage()
},
// 建立连接
connect() {
console.log(this.surl);
uni.connectSocket({
url: this.surl,
method: 'GET'
});
},
// 打开Soceket
openSocket() {
let that = this
uni.onSocketOpen((res) => {
that.socketOpen = true
console.log('WebSocket连接已打开!');
});
},
// 监听关闭
onclose() {
let that = this
uni.onSocketClose((res) => {
that.socketOpen = false
console.log('WebSocket 已关闭!');
});
},
// 关闭
closeSocket() {
uni.closeSocket();
},
// 接收事件
onSocketMessage() {
let that = this
uni.onSocketMessage((res) => {
let obj = JSON.parse(res.data)
console.log("接收事件", obj);
this.onMessageHandle(obj)
});
},
// 接收---到事件后处理的方法
onMessageHandle(obj) {
var data = JSON.parse(obj.content)
this.list.push({
userType: 'friend',
avatar: this._friendAvatar,
content: data.text
})
// 滚动到底部
this.scrollToBottom()
},
// 发送---消息后处理的方法
sendMessageHandle(obj) {
var data = obj.content
this.list.push({
userType: 'self',
avatar: this._selfAvatar,
content: data.text
})
// 滚动到底部
this.scrollToBottom()
},
// 定位到底部
scrollToBottom() {
this.$nextTick(() => {
this.scrollIntoView = "last-msg-item"
this.$nextTick(() => {
this.scrollIntoView = ""
})
})
},
// 开启键盘高度的监听
listenerKeyboardHeight() {
this.listener = (res) => {
console.log("键盘高度", res.height)
this.keyboardHeight = res.height
this.$nextTick(() => {
this.scrollToBottom()
})
}
uni.onKeyboardHeightChange(this.listener)
}
}
}
聊天界面组件
接下来,在聊天界面中引入上面创建的混入模块,并实现聊天功能。
聊天界面代码
<template>
<view class="page">
<scroll-view class="scroll-view" scroll-y scroll-with-animation :scroll-top="top">
<view style="padding: 30rpx 30rpx 240rpx;">
<view class="message" :class="[item.userType]" v-for="(item,index) in list" :key="index">
<image :src="item.avatar" v-if="item.userType === 'friend'" class="avatar" mode="widthFix"></image>
<view class="content" v-if="item.messageType === 'image'">
<image :src="item.content" mode="widthFix"></image>
</view>
<view class="content" v-else>
{{ item.content }}
</view>
<image :src="item.avatar" v-if="item.userType === 'self'" class="avatar" mode="widthFix"></image>
</view>
</view>
</scroll-view>
<view class="tool">
<input type="text" v-model="content" class="input" />
<view @click="sendSocketMessage(content)">发送</view>
</view>
<view id="last-msg-item" style="height: 1px;"></view>
</view>
</template>
<script>
import { socket } from "@/mixins/socket.js"
export default {
mixins: [socket],
data() {
return {
content: '',
list: [],
top: 0
};
},
onLoad(options) {
uni.setNavigationBarTitle({
title: options.name
})
this._friendAvatar = 'https://jiejinda.oss-cn-beijing.aliyuncs.com/d66c27ff806a7c51e56bbd2b402bcdc2e2b8244fbca7481014a0cbb474b1d78f.png'
this._selfAvatar = 'https://jiejinda.oss-cn-beijing.aliyuncs.com/157422b1c0c4e79ffb4e1d84bb20b76a1849a4b93d08637b8d49567aa5968ceb.png'
this.list = [
{
content: '欠我的工资什么时候还',
userType: 'friend',
avatar: this._friendAvatar
},{
content: '马上还!',
userType: 'self',
avatar: this._selfAvatar
},{
content: '还完拉黑你',
userType: 'self',
avatar: this._selfAvatar
}
]
},
methods: {
scrollToBottom() {
this.top = this.list.length * 1000
}
}
}
</script>
<style lang="scss" scoped>
.scroll-view {
/* #ifdef H5 */
height: calc(100vh - 44px);
/* #endif */
/* #ifndef H5 */
height: 100vh;
/* #endif */
background: #eee;
box-sizing: border-box;
}
.message {
display: flex;
align-items: flex-start;
margin-bottom: 30rpx;
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
margin-right: 30rpx;
}
.content {
min-height: 80rpx;
max-width: 60vw;
box-sizing: border-box;
font-size: 28rpx;
line-height: 1.3;
padding: 20rpx;
border-radius: 10rpx;
background: #fff;
image {
width: 200rpx;
}
}
&.self {
justify-content: flex-end;
.avatar {
margin: 0 0 0 30rpx;
}
.content {
position: relative;
&::after {
position: absolute;
content: '';
width: 0;
height: 0;
border: 16rpx solid transparent;
border-left: 16rpx solid #fff;
right: -28rpx;
top: 24rpx;
}
}
}
&.friend {
.content {
position: relative;
&::after {
position: absolute;
content: '';
width: 0;
height: 0;
border: 16rpx solid transparent;
border-right: 16rpx solid #fff;
left: -28rpx;
top: 24rpx;
}
}
}
}
.tool {
position: fixed;
width: 100%;
min-height: 120rpx;
left: 0;
bottom: 0;
background: #fff;
display: flex;
justify-content: space-between;
align-items: flex-start;
box-sizing: border-box;
padding: 20rpx 24rpx 20rpx 40rpx;
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom)/2) !important;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom)/2) !important;
.input {
background: #eee;
border-radius: 10rpx;
height: 70rpx;
margin-right: 30rpx;
flex: 1;
padding: 0 20rpx;
box-sizing: border-box;
font-size: 28rpx;
}
>view {
width: 150rpx;
line-height: 70rpx;
text-align: center;
height: 70rpx;
background-color: #eee;
border-radius: 10rpx;
}
}
</style>
最终效果
注意查看控制台
查看network的messages,往下的箭头就是接受消息,往上的箭头就是发送的消息
总结
通过以上步骤,我们成功地实现了一个基于uni-app的实时聊天功能。在这个过程中,我们学习了如何使用WebSocket进行实时通信,以及如何处理