我在本学期的软件工程课的大作业中第一次接触微信小程序的云开发功能,云开发的模式可以实现一部分的网络功能,腾讯爸爸会给开发者提供一个云存储空间、一个云数据库,开发者可以自己写一些云函数进行上传。我们的本意是开发一个二手交易平台,这个平台需要一个聊天系统,但由于项目太托,到最后也没有搭服务器,无奈之下准备取消这个功能。但鉴于学习小程序的时候第一次写得是贪吃蛇,于是从贪吃蛇的代码中找到了灵感。
作为大二狗,之前只学了C和Java,只在作业的过程中用过少量的js和C#,所以了解得并不深入。此块如有错误请各位前辈批评指正。我们实现实时聊天就必定少不了实时更新界面,不论是网页版的贪吃蛇还是微信小程序版的贪吃蛇,都需要一个计时器,这里选择js中的setInterval()这个函数,它是一个计时器,可以有两个参数,第一个参数是一个函数,第二个参数代表时间,比如var t = setInterval(f(),3000),就是说每隔三秒钟执行一次f(),t代表这个计时器,可以在不用的时候关掉(比如退出聊天页之后),避免过多的资源消耗。进行过微信小程序云开发的人都知道,云开发提供的API可以用一个函数直接访问到数据库,我们结合这些API和计时器,就可以实现实时获取数据库内消息更新的需求,再用最新的数据库信息刷新页面数据,就可以实现实时通信。这似乎回到了上古时代每隔一段时间就向服务器你发送一次请求的机制,但它确实是行之有效的,如果为了省时间,又不影响核心需求,你也可以用这样的形式实现一个聊天室。这种方法为了避免资源的过多消耗,一定会有一个延时,这个延时的绝大部分取决于我们在计时器中设定的时间,延时越长,资源消耗越少,但是用户的体验也会越差。这种实时通信仅仅适用于规模较小的轻量级小程序。
个人认为仅仅在聊天页进行调用计时器就好了,在onHide()的时候吧计时器关掉。如果你希望保留所有的聊天记录就不得不自己去写云函数,因为微信小程序的云开发对数据库的调用有这样一个限制:当你在本地调用查询的时候,数据库一次最多只能返回20条结果,如果使用云函数的话,一次可以有100条,如果100条无法满足你的需求的话,你可以promise一下分批提取然后再进行手动的排序(这里我没有用到,但是微信小程序的官方文档里是这么写的)。没有申请提升环境资源上限的开发者一个月只能调用20万次云函数,这是相当有限的,所以如果你的小程序像我一样是做作业,最终不会上线,没有申请提升上限的需要,请珍惜这个资源。
这个前端的wxml和wxss代码我自己写出来还是比较满意的,至于js可以实现实时聊天没错,但是由于整个平台是几天赶出来的项目,肯定会有一些小问题,也是当时没有来得及完善的问题。比如说偶尔会有消息发送出去了但是消息栏中的内容还没有清空;比如经常会有新消息出来了但是滚动条没有下滑到最底端;比如发消息的延时没有给用户显示消息发送的提醒影响用户体验等等。下面我把自己这个项目中的聊天页的代码放在这里,供大家参考和批评指正:
wxml:
发送
wxss:
::-webkit-scrollbar{
width: 0;
height: 0;
color: transparent;
}
.chat{
position: fixed;
top: 0rpx;
width: 100%;
overflow: scroll;
height: 90%;
}
.message{
width: 100%;
}
.image_true{
margin: 10rpx;
float: right;
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: red;
}
.text_true{
margin-top: 15rpx;
margin-bottom: 10rpx;
padding: 3%;
display: block;
width: 520rpx;
float: right;
border: 1rpx black solid;
border-radius: 5rpx;
color: black;
background-color: white;
word-break: break-all;
}
.image_false{
margin: 10rpx;
float: left;
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: red;
}
.text_false{
margin-top: 15rpx;
margin-bottom: 10rpx;
padding: 3%;
display: block;
width: 520rpx;
float: left;
border: 1rpx black solid;
border-radius: 5rpx;
color: white;
background-color: #66ffcc;
word-break: break-all;
}
.input{
width: 100%;
height: 10%;
background-color: #66ffcc;
position: fixed;
bottom: 0rpx;
overflow: scroll;
}
.inputarea{
width: 495rpx;
height: 6%;
margin-top: 2%;
background-color: white;
padding-left: 20rpx;
border-radius: 20rpx;
position: fixed;
bottom: 2%;
left: 4%;
}
.submit{
border: 4rpx white solid;
height: 6%;
width: 140rpx;
color: white;
font-size: 100%;
line-height: 200%;
border-radius: 20%;
position: fixed;
right: 4%;
bottom: 2%;
text-align: center;
}
js:
Page({
/**
* 页面的初始数据
*/
data: {
topScroll: 100000000000000000,
input_value: '',
goodID: '',
myID: '',
anotherID: '',
avatarImg_true: '',
avatarImg_false: '../../images/another_avatar.jpg',
messageList: [],
count: 0,
timer: '',
imageCloudPath: '',
goodName: '',
anotherName: ''
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if(options.type == 1){
this.setData({
avatarImg_true: getApp().globalData.avatarUrl,
myID: getApp().globalData.openid,
anotherID: options.userID,
goodID: options.goodID,
imageCloudPath: options.imagePath,
goodName: options.goodName,
anotherName: options.userNickName
})
}
if (options.type == 2) {
this.setData({
avatarImg_true: getApp().globalData.avatarUrl,
myID: getApp().globalData.openid,
anotherID: options.id,
goodID: options.goodID,
imageCloudPath: options.imagePath,
goodName: options.goodName,
anotherName: options.userNickName
})
}
const db = wx.cloud.database()
db.collection('user').where({
_openid: this.data.anotherID
}).get({
success: res=>{
console.log(res.data[0].avatar)
this.setData({ avatarImg_false: res.data[0].avatar})
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
var _this = this
this.data.timer = setInterval(function () {
const db = wx.cloud.database()
const _ = db.command
var chat = db.collection('chat').limit(100).where(_.or([
{
goodID: _this.data.goodID,
speaker: _this.data.myID,
listenner: _this.data.anotherID
},
{
goodID: _this.data.goodID,
speaker: _this.data.anotherID,
listenner: _this.data.myID
}
])).orderBy('time', 'desc')
chat.get({
success: res => {
var list = []
list = res.data
console.log(list)
for(var i=0;i{
console.log(res)
},
fail: function(){
wx.showToast({
title: '消息未发送',
icon: 'none',
duration: 2000
})
}
})
this.setData({input_value: ''})
wx.hideLoading()
},
isEmpty: function (str) {
if (str == null) return true;
return str.replace(/\s/g, '').length == 0;
}
})
云函数:send(新手使用的话上传之前别忘了安装依赖,具体步骤官方文档里写得很清楚)
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
// 云函数入口函数
exports.main = async (event, context) => {
try{
await db.collection('chat').add({
data:{
goodID: event.goodID,
speaker: event.speaker,
listenner: event.listenner,
content: event.content,
time: event.time,
hasRead: event.hasRead,
speakerName: event.speakerName,
listennerName: event.listennerName,
imageCloudPath: event.imageCloudPath,
goodName: event.goodName
}
})
return 'true'
}
catch(e){
console.log(e)
}
}