随着移动互联网的发展,即时通讯应用变得越来越普遍。本文将介绍如何使用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)
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
聊天界面组件
接下来,在聊天界面中引入上面创建的混入模块,并实现聊天功能。
聊天界面代码
<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>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
最终效果
注意查看控制台
查看network的messages,往下的箭头就是接受消息,往上的箭头就是发送的消息
总结
通过以上步骤,我们成功地实现了一个基于uni-app的实时聊天功能。在这个过程中,我们学习了如何使用WebSocket进行实时通信,以及如何处理