Browse Source

优化: uniapp拉取过多离线消息导致卡顿

master
xsx 2 years ago
parent
commit
aac6eb0d1c
  1. 5
      im-ui/src/store/chatStore.js
  2. 21
      im-uniapp/components/chat-message-item/chat-message-item.vue
  3. 4
      im-uniapp/components/pop-menu/pop-menu.vue
  4. 22
      im-uniapp/pages/chat/chat-box.vue
  5. 147
      im-uniapp/store/chatStore.js

5
im-ui/src/store/chatStore.js

@ -186,7 +186,12 @@ export default {
} }
} }
} }
if(insertPos == chat.messages.length){
// 这种赋值效率最高
chat.messages[insertPos]= msgInfo;
}else{
chat.messages.splice(insertPos, 0, msgInfo); chat.messages.splice(insertPos, 0, msgInfo);
}
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateMessage(state, msgInfo) { updateMessage(state, msgInfo) {

21
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -11,16 +11,17 @@
:class="{'chat-msg-mine':msgInfo.selfSend}"> :class="{'chat-msg-mine':msgInfo.selfSend}">
<head-image class="avatar" @longpress.prevent="$emit('longPressHead')" :id="msgInfo.sendId" :url="headImage" <head-image class="avatar" @longpress.prevent="$emit('longPressHead')" :id="msgInfo.sendId" :url="headImage"
:name="showName" :size="80"></head-image> :name="showName" :size="80"></head-image>
<view class="chat-msg-content" @longpress="onShowMenu($event)"> <view class="chat-msg-content">
<view v-if="msgInfo.groupId && !msgInfo.selfSend" class="chat-msg-top"> <view v-if="msgInfo.groupId && !msgInfo.selfSend" class="chat-msg-top">
<text>{{showName}}</text> <text>{{showName}}</text>
</view> </view>
<view class="chat-msg-bottom"> <view class="chat-msg-bottom" @touchmove="onHideMenu()">
<rich-text class="chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT" <rich-text class="chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT"
:nodes="$emo.transform(msgInfo.content)"></rich-text> :nodes="$emo.transform(msgInfo.content)"
@longpress="onShowMenu($event)"></rich-text>
<view class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE"> <view class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE">
<view class="img-load-box"> <view class="img-load-box" @longpress="onShowMenu($event)">
<image class="send-image" mode="heightFix" :src="JSON.parse(msgInfo.content).thumbUrl" <image class="send-image" mode="heightFix" :src="JSON.parse(msgInfo.content).thumbUrl"
lazy-load="true" @click.stop="onShowFullImage()"> lazy-load="true" @click.stop="onShowFullImage()">
</image> </image>
@ -30,7 +31,7 @@
class="send-fail iconfont icon-warning-circle-fill"></text> class="send-fail iconfont icon-warning-circle-fill"></text>
</view> </view>
<view class="chat-msg-file" v-if="msgInfo.type==$enums.MESSAGE_TYPE.FILE"> <view class="chat-msg-file" v-if="msgInfo.type==$enums.MESSAGE_TYPE.FILE">
<view class="chat-file-box"> <view class="chat-file-box" @longpress="onShowMenu($event)">
<view class="chat-file-info"> <view class="chat-file-info">
<uni-link class="chat-file-name" :text="data.name" showUnderLine="true" color="#007BFF" <uni-link class="chat-file-name" :text="data.name" showUnderLine="true" color="#007BFF"
:href="data.url"></uni-link> :href="data.url"></uni-link>
@ -43,13 +44,14 @@
class="send-fail iconfont icon-warning-circle-fill"></text> class="send-fail iconfont icon-warning-circle-fill"></text>
</view> </view>
<view class="chat-msg-audio chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.AUDIO" <view class="chat-msg-audio chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.AUDIO"
@click="onPlayAudio()"> @click="onPlayAudio()" @longpress="onShowMenu($event)">
<text class="iconfont icon-voice-play"></text> <text class="iconfont icon-voice-play"></text>
<text class="chat-audio-text">{{JSON.parse(msgInfo.content).duration+'"'}}</text> <text class="chat-audio-text">{{JSON.parse(msgInfo.content).duration+'"'}}</text>
<text v-if="audioPlayState=='PAUSE'" class="iconfont icon-play"></text> <text v-if="audioPlayState=='PAUSE'" class="iconfont icon-play"></text>
<text v-if="audioPlayState=='PLAYING'" class="iconfont icon-pause"></text> <text v-if="audioPlayState=='PLAYING'" class="iconfont icon-pause"></text>
</view> </view>
<view class="chat-realtime chat-msg-text" v-if="isRTMessage" @click="$emit('call')"> <view class="chat-realtime chat-msg-text" v-if="isRTMessage"
@click="$emit('call')" @longpress="onShowMenu($event)">
<text v-if="msgInfo.type==$enums.MESSAGE_TYPE.RT_VOICE" class="iconfont icon-chat-voice"></text> <text v-if="msgInfo.type==$enums.MESSAGE_TYPE.RT_VOICE" class="iconfont icon-chat-voice"></text>
<text v-if="msgInfo.type==$enums.MESSAGE_TYPE.RT_VIDEO" class="iconfont icon-chat-video"></text> <text v-if="msgInfo.type==$enums.MESSAGE_TYPE.RT_VIDEO" class="iconfont icon-chat-video"></text>
<text>{{msgInfo.content}}</text> <text>{{msgInfo.content}}</text>
@ -68,7 +70,7 @@
</view> </view>
</view> </view>
<chat-group-readed ref="chatGroupReaded" :groupMembers="groupMembers" :msgInfo="msgInfo"></chat-group-readed> <chat-group-readed ref="chatGroupReaded" :groupMembers="groupMembers" :msgInfo="msgInfo"></chat-group-readed>
<pop-menu v-if="menu.show" :menu-style="menu.style" :items="menuItems" @close="menu.show=false" <pop-menu v-if="menu.show" :menu-style="menu.style" :items="menuItems" @close="onHideMenu()"
@select="onSelectMenu"></pop-menu> @select="onSelectMenu"></pop-menu>
</view> </view>
</template> </template>
@ -129,6 +131,9 @@
} }
}) })
}, },
onHideMenu(){
this.menu.show = false;
},
onSendFail() { onSendFail() {
uni.showToast({ uni.showToast({
title: "该文件已发送失败,目前不支持自动重新发送,建议手动重新发送", title: "该文件已发送失败,目前不支持自动重新发送,建议手动重新发送",

4
im-uniapp/components/pop-menu/pop-menu.vue

@ -1,11 +1,10 @@
<template> <template>
<view class="pop-menu" @tap="onClose()" @contextmenu.prevent=""> <view class="pop-menu" @tap="onClose()" @touchmove="onClose" @contextmenu.prevent="">
<view class="menu" :style="menuStyle"> <view class="menu" :style="menuStyle">
<view class="menu-item" v-for="(item) in items" :key="item.key" @click.prevent="onSelectMenu(item)"> <view class="menu-item" v-for="(item) in items" :key="item.key" @click.prevent="onSelectMenu(item)">
<uni-icons :type="item.icon" size="22"></uni-icons> <uni-icons :type="item.icon" size="22"></uni-icons>
<text> {{item.name}}</text> <text> {{item.name}}</text>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
@ -29,6 +28,7 @@
this.$emit("select", item); this.$emit("select", item);
}, },
onClose() { onClose() {
console.log("@touchmove")
this.$emit("close"); this.$emit("close");
} }
} }

22
im-uniapp/pages/chat/chat-box.vue

@ -6,7 +6,8 @@
<uni-icons class="btn-side right" type="more-filled" size="30" @click="onShowMore()"></uni-icons> <uni-icons class="btn-side right" type="more-filled" size="30" @click="onShowMore()"></uni-icons>
</view> </view>
<view class="chat-msg" @click="switchChatTabBox('none',true)"> <view class="chat-msg" @click="switchChatTabBox('none',true)">
<scroll-view class="scroll-box" scroll-y="true" @scrolltoupper="onScrollToTop" <scroll-view class="scroll-box" scroll-y="true"
upper-threshold="200" @scrolltoupper="onScrollToTop"
:scroll-into-view="'chat-item-'+scrollMsgIdx"> :scroll-into-view="'chat-item-'+scrollMsgIdx">
<view v-for="(msgInfo,idx) in chat.messages" :key="idx"> <view v-for="(msgInfo,idx) in chat.messages" :key="idx">
<chat-message-item v-if="idx>=showMinIdx" :headImage="headImage(msgInfo)" @call="onRtCall(msgInfo)" <chat-message-item v-if="idx>=showMinIdx" :headImage="headImage(msgInfo)" @call="onRtCall(msgInfo)"
@ -136,7 +137,6 @@
this.switchChatTabBox('none',false); this.switchChatTabBox('none',false);
}, },
onSendRecord(data) { onSendRecord(data) {
console.log(data);
let msgInfo = { let msgInfo = {
content: JSON.stringify(data), content: JSON.stringify(data),
type: this.$enums.MESSAGE_TYPE.AUDIO, type: this.$enums.MESSAGE_TYPE.AUDIO,
@ -496,12 +496,20 @@
}); });
}, },
onScrollToTop() { onScrollToTop() {
// #ifdef MP if(this.showMinIdx==0){
uni.showToast({
title: "没有更多消息啦",
icon: "none"
})
return;
}
// #ifndef H5
// //
this.scrollToMsgIdx(this.showMinIdx); this.scrollToMsgIdx(this.showMinIdx);
// #endif // #endif
// 10 // 0
this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0; this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
}, },
onShowMore() { onShowMore() {
if (this.chat.type == "GROUP") { if (this.chat.type == "GROUP") {
@ -649,9 +657,9 @@
onLoad(options) { onLoad(options) {
// //
this.chat = this.$store.state.chatStore.chats[options.chatIdx]; this.chat = this.$store.state.chatStore.chats[options.chatIdx];
// 30 // 20
let size = this.chat.messages.length; let size = this.chat.messages.length;
this.showMinIdx = size > 30 ? size - 30 : 0; this.showMinIdx = size > 20 ? size - 20 : 0;
// //
this.$store.commit("activeChat", options.chatIdx); this.$store.commit("activeChat", options.chatIdx);
// //

147
im-uniapp/store/chatStore.js

@ -1,10 +1,14 @@
import { MESSAGE_TYPE, MESSAGE_STATUS } from '@/common/enums.js'; import {
MESSAGE_TYPE,
MESSAGE_STATUS
} from '@/common/enums.js';
import userStore from './userStore'; import userStore from './userStore';
export default { export default {
state: { state: {
activeIndex: -1, activeIndex: -1,
chats: [], chats: [],
copyChats: [],
privateMsgMaxId: 0, privateMsgMaxId: 0,
groupMsgMaxId: 0, groupMsgMaxId: 0,
loadingPrivateMsg: false, loadingPrivateMsg: false,
@ -24,13 +28,16 @@ export default {
} }
}) })
}) })
// 拷贝一份,用于缓存离线消息
state.copyChats = JSON.parse(JSON.stringify(state.chats))
}, },
openChat(state, chatInfo) { openChat(state, chatInfo) {
let chats = this.getters.findChats();
let chat = null; let chat = null;
for (let idx in state.chats) { for (let idx in chats) {
if (state.chats[idx].type == chatInfo.type && if (chats[idx].type == chatInfo.type &&
state.chats[idx].targetId === chatInfo.targetId) { chats[idx].targetId === chatInfo.targetId) {
chat = state.chats[idx]; chat = chats[idx];
// 放置头部 // 放置头部
this.commit("moveTop", idx) this.commit("moveTop", idx)
break; break;
@ -50,38 +57,40 @@ export default {
atMe: false, atMe: false,
atAll: false atAll: false
}; };
state.chats.unshift(chat); chats.unshift(chat);
} }
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
activeChat(state, idx) { activeChat(state, idx) {
let chats = this.getters.findChats();
state.activeIndex = idx; state.activeIndex = idx;
if (idx >= 0) { if (idx >= 0) {
state.chats[idx].unreadCount = 0; chats[idx].unreadCount = 0;
} }
}, },
resetUnreadCount(state, chatInfo) { resetUnreadCount(state, chatInfo) {
for (let idx in state.chats) { let chats = this.getters.findChats();
if (state.chats[idx].type == chatInfo.type && for (let idx in chats) {
state.chats[idx].targetId == chatInfo.targetId) { if (chats[idx].type == chatInfo.type &&
state.chats[idx].unreadCount = 0; chats[idx].targetId == chatInfo.targetId) {
state.chats[idx].atMe = false; chats[idx].unreadCount = 0;
state.chats[idx].atAll = false; chats[idx].atMe = false;
chats[idx].atAll = false;
} }
} }
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
readedMessage(state, pos) { readedMessage(state, pos) {
for (let idx in state.chats) { let chats = this.getters.findChats();
if (state.chats[idx].type == 'PRIVATE' && for (let idx in chats) {
state.chats[idx].targetId == pos.friendId) { if (chats[idx].type == 'PRIVATE' &&
state.chats[idx].messages.forEach((m) => { chats[idx].targetId == pos.friendId) {
chats[idx].messages.forEach((m) => {
if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) { if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) {
// pos.maxId为空表示整个会话已读 // pos.maxId为空表示整个会话已读
if (!pos.maxId || m.id <= pos.maxId) { if (!pos.maxId || m.id <= pos.maxId) {
m.status = MESSAGE_STATUS.READED m.status = MESSAGE_STATUS.READED
} }
} }
}) })
} }
@ -89,26 +98,25 @@ export default {
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
removeChat(state, idx) { removeChat(state, idx) {
state.chats.splice(idx, 1); let chats = this.getters.findChats();
chats.splice(idx, 1);
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
removePrivateChat(state, userId) { removePrivateChat(state, userId) {
for (let idx in state.chats) { let chats = this.getters.findChats();
if (state.chats[idx].type == 'PRIVATE' && for (let idx in chats) {
state.chats[idx].targetId == userId) { if (chats[idx].type == 'PRIVATE' &&
chats[idx].targetId == userId) {
this.commit("removeChat", idx); this.commit("removeChat", idx);
} }
} }
}, },
moveTop(state, idx) { moveTop(state, idx) {
// 加载中不移动,很耗性能 let chats = this.getters.findChats();
if(state.loadingPrivateMsg || state.loadingGroupMsg){
return ;
}
if (idx > 0) { if (idx > 0) {
let chat = state.chats[idx]; let chat = chats[idx];
state.chats.splice(idx, 1); chats.splice(idx, 1);
state.chats.unshift(chat); chats.unshift(chat);
this.commit("saveToStorage"); this.commit("saveToStorage");
} }
}, },
@ -135,7 +143,6 @@ export default {
return; return;
} }
// 会话列表内容 // 会话列表内容
if(!state.loadingPrivateMsg && !state.loadingGroupMsg){
if (msgInfo.type == MESSAGE_TYPE.IMAGE) { if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
chat.lastContent = "[图片]"; chat.lastContent = "[图片]";
} else if (msgInfo.type == MESSAGE_TYPE.FILE) { } else if (msgInfo.type == MESSAGE_TYPE.FILE) {
@ -151,15 +158,15 @@ export default {
} }
chat.lastSendTime = msgInfo.sendTime; chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = msgInfo.sendNickName; chat.sendNickName = msgInfo.sendNickName;
}
// 未读加1 // 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
&& msgInfo.type != MESSAGE_TYPE.TIP_TEXT) { msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
chat.unreadCount++; chat.unreadCount++;
} }
// 是否有人@我 // 是否有人@我
if(!msgInfo.selfSend && chat.type=="GROUP" && msgInfo.atUserIds if (!msgInfo.selfSend && chat.type == "GROUP" && msgInfo.atUserIds &&
&& msgInfo.status != MESSAGE_STATUS.READED){ msgInfo.status != MESSAGE_STATUS.READED) {
let userId = userStore.state.userInfo.id; let userId = userStore.state.userInfo.id;
if (msgInfo.atUserIds.indexOf(userId) >= 0) { if (msgInfo.atUserIds.indexOf(userId) >= 0) {
chat.atMe = true; chat.atMe = true;
@ -188,7 +195,12 @@ export default {
} }
} }
} }
if(insertPos == chat.messages.length){
// 这种赋值效率最高
chat.messages[insertPos]= msgInfo;
}else{
chat.messages.splice(insertPos, 0, msgInfo); chat.messages.splice(insertPos, 0, msgInfo);
}
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateMessage(state, msgInfo) { updateMessage(state, msgInfo) {
@ -220,8 +232,9 @@ export default {
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateChatFromFriend(state, friend) { updateChatFromFriend(state, friend) {
for (let i in state.chats) { let chats = this.getters.findChats();
let chat = state.chats[i]; for (let i in chats) {
let chat = chats[i];
if (chat.type == 'PRIVATE' && chat.targetId == friend.id) { if (chat.type == 'PRIVATE' && chat.targetId == friend.id) {
chat.headImage = friend.headImageThumb; chat.headImage = friend.headImageThumb;
chat.showName = friend.nickName; chat.showName = friend.nickName;
@ -231,8 +244,9 @@ export default {
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateChatFromGroup(state, group) { updateChatFromGroup(state, group) {
for (let i in state.chats) { let chats = this.getters.findChats();
let chat = state.chats[i]; for (let i in chats) {
let chat = chats[i];
if (chat.type == 'GROUP' && chat.targetId == group.id) { if (chat.type == 'GROUP' && chat.targetId == group.id) {
chat.headImage = group.headImageThumb; chat.headImage = group.headImageThumb;
chat.showName = group.remark; chat.showName = group.remark;
@ -254,29 +268,15 @@ export default {
} }
}, },
refreshChats(state) { refreshChats(state) {
state.chats.forEach((chat)=>{ // 将离线消息一次性装载回来
if(chat.messages.length>0){ state.chats = JSON.parse(JSON.stringify(state.copyChats))
let msgInfo = chat.messages[chat.messages.length-1]; this.commit("saveToStorage");
if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
chat.lastContent = "[图片]";
} else if (msgInfo.type == MESSAGE_TYPE.FILE) {
chat.lastContent = "[文件]";
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]";
} else if (msgInfo.type == MESSAGE_TYPE.TEXT || msgInfo.type == MESSAGE_TYPE.RECALL) {
chat.lastContent = msgInfo.content;
}
chat.lastSendTime = msgInfo.sendTime;
}else{
chat.lastContent = "";
chat.lastSendTime = new Date().getTime()
}
})
state.chats.sort((chat1, chat2) => {
return chat2.lastSendTime-chat1.lastSendTime;
});
}, },
saveToStorage(state) { saveToStorage(state) {
// 加载中不保存,防止卡顿
if(state.loadingPrivateMsg || state.loadingGroupMsg){
return;
}
let userId = userStore.state.userInfo.id; let userId = userStore.state.userInfo.id;
let key = "chats-" + userId; let key = "chats-" + userId;
let chatsData = { let chatsData = {
@ -291,6 +291,7 @@ export default {
}, },
clear(state) { clear(state) {
state.chats = []; state.chats = [];
state.copyChats = [];
state.activeIndex = -1; state.activeIndex = -1;
state.privateMsgMaxId = 0; state.privateMsgMaxId = 0;
state.groupMsgMaxId = 0; state.groupMsgMaxId = 0;
@ -316,24 +317,32 @@ export default {
} }
}, },
getters: { getters: {
findChatIdx: (state) => (chat) => { findChats: (state) => () => {
for (let idx in state.chats) { /* uniapp线state.copyChats
if (state.chats[idx].type == chat.type && 等待所有离线消息拉取完成后再统一进行渲染 */
state.chats[idx].targetId === chat.targetId) { let isLoading = state.loadingPrivateMsg || state.loadingGroupMsg;
return isLoading ? state.copyChats : state.chats;
},
findChatIdx: (state, getters) => (chat) => {
let chats = getters.findChats();
for (let idx in chats) {
if (chats[idx].type == chat.type &&
chats[idx].targetId === chat.targetId) {
chat = state.chats[idx]; chat = state.chats[idx];
return idx; return idx;
} }
} }
}, },
findChat: (state) => (msgInfo) => { findChat: (state, getters) => (msgInfo) => {
let chats = getters.findChats();
// 获取对方id或群id // 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId; let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId;
let chat = null; let chat = null;
for (let idx in state.chats) { for (let idx in chats) {
if (state.chats[idx].type == type && if (chats[idx].type == type &&
state.chats[idx].targetId === targetId) { chats[idx].targetId === targetId) {
chat = state.chats[idx]; chat = chats[idx];
break; break;
} }
} }

Loading…
Cancel
Save