Browse Source

优化: uniapp会话置顶、删除会话、好友状态更新导致卡顿

master
blue 2 years ago
parent
commit
267e790188
  1. 2
      im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java
  2. 2
      im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java
  3. 3
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  4. 9
      im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java
  5. 7
      im-ui/src/store/chatStore.js
  6. 4
      im-uniapp/App.vue
  7. 18
      im-uniapp/components/chat-item/chat-item.vue
  8. 8
      im-uniapp/components/chat-message-item/chat-message-item.vue
  9. 1
      im-uniapp/components/pop-menu/pop-menu.vue
  10. 2
      im-uniapp/components/user-search/user-search.vue
  11. 3
      im-uniapp/package.json
  12. 15
      im-uniapp/pages/chat/chat-box.vue
  13. 42
      im-uniapp/pages/chat/chat.vue
  14. 5
      im-uniapp/pages/common/user-info.vue
  15. 2
      im-uniapp/pages/friend/friend-add.vue
  16. 5
      im-uniapp/pages/friend/friend-search.vue
  17. 17
      im-uniapp/pages/friend/friend.vue
  18. 3
      im-uniapp/pages/group/group-info.vue
  19. 3
      im-uniapp/pages/group/group-invite.vue
  20. 77
      im-uniapp/store/chatStore.js
  21. 55
      im-uniapp/store/friendStore.js

2
im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java

@ -37,7 +37,7 @@ public class GroupMessageController {
@GetMapping("/loadMessage")
@ApiOperation(value = "拉取消息", notes = "拉取消息,一次最多拉取100条")
@ApiOperation(value = "拉取消息(已废弃)", notes = "拉取消息,一次最多拉取100条")
public Result<List<GroupMessageVO>> loadMessage(@RequestParam Long minId) {
return ResultUtils.success(groupMessageService.loadMessage(minId));
}

2
im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java

@ -38,7 +38,7 @@ public class PrivateMessageController {
@GetMapping("/loadMessage")
@ApiOperation(value = "拉取消息", notes = "拉取消息,一次最多拉取100条")
@ApiOperation(value = "拉取消息(已废弃)", notes = "拉取消息,一次最多拉取100条")
public Result<List<PrivateMessageVO>> loadMessage(@RequestParam Long minId) {
return ResultUtils.success(privateMessageService.loadMessage(minId));
}

3
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -237,8 +237,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setData(msgInfo);
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(session.getUserId());
sendMessage.setSendToSelf(false);
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
imClient.sendPrivateMessage(sendMessage);
// 推送回执消息给对方,更新已读状态

9
im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java

@ -79,11 +79,10 @@ public final class SensitiveFilterUtil {
*/
@PostConstruct
public void init() {
try (
// 类加载器
InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
) {
try {
// 类加载器
InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String keyword;
while ((keyword = reader.readLine()) != null) {
// 添加到前缀树

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

@ -186,12 +186,7 @@ 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");
},
updateMessage(state, msgInfo) {

4
im-uniapp/App.vue

@ -63,12 +63,14 @@
})
},
pullPrivateOfflineMessage(minId) {
store.commit("loadingPrivateMsg",true)
http({
url: "/message/private/pullOfflineMessage?minId=" + minId,
method: 'get'
});
},
pullGroupOfflineMessage(minId) {
store.commit("loadingGroupMsg",true)
http({
url: "/message/group/pullOfflineMessage?minId=" + minId,
method: 'get'
@ -195,7 +197,7 @@
},
loadFriendInfo(id) {
return new Promise((resolve, reject) => {
let friend = store.state.friendStore.friends.find((f) => f.id == id);
let friend = store.getters.findFriend(id);
if (friend) {
resolve(friend);
} else {

18
im-uniapp/components/chat-item/chat-item.vue

@ -1,5 +1,5 @@
<template>
<view class="chat-item" @click="showChatBox()">
<view class="chat-item" :class="active?'active':''" @click="showChatBox()">
<view class="left">
<head-image :url="chat.headImage" :name="chat.showName" :size="90"></head-image>
<view v-if="chat.unreadCount>0" class="unread-text">{{chat.unreadCount}}</view>
@ -30,6 +30,10 @@
},
index: {
type: Number
},
active: {
type: Boolean,
default: false
}
},
methods: {
@ -68,6 +72,10 @@
background-color: #eeeeee;
}
&.active {
background-color: #eeeeee;
}
.left {
position: relative;
display: flex;
@ -100,8 +108,8 @@
.chat-name {
display: flex;
line-height: 50rpx;
height: 50rpx;
line-height: 44rpx;
height: 44rpx;
.chat-name-text {
flex: 1;
@ -122,7 +130,8 @@
.chat-content {
display: flex;
line-height: 44rpx;
line-height: 60rpx;
height: 60rpx;
.chat-at-text {
color: #c70b0b;
font-size: 24rpx;
@ -137,7 +146,6 @@
font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
line-height: 50rpx;
text-overflow: ellipsis;
}
}

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

@ -17,9 +17,11 @@
</view>
<view class="chat-msg-bottom" @touchmove="onHideMenu()">
<rich-text class="chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT"
:nodes="$emo.transform(msgInfo.content)"
@longpress="onShowMenu($event)"></rich-text>
<view v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT" @longpress.native="onShowMenu($event)">
<rich-text class="chat-msg-text"
:nodes="$emo.transform(msgInfo.content)"
></rich-text>
</view>
<view class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE">
<view class="img-load-box" @longpress="onShowMenu($event)">
<image class="send-image" mode="heightFix" :src="JSON.parse(msgInfo.content).thumbUrl"

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

@ -28,7 +28,6 @@
this.$emit("select", item);
},
onClose() {
console.log("@touchmove")
this.$emit("close");
}
}

2
im-uniapp/components/user-search/user-search.vue

@ -68,7 +68,7 @@
isFriend(userId) {
let friends = this.$store.state.friendStore.friends;
let friend = friends.find((f) => f.id == userId);
return friend != undefined;
return friend&&!friend.delete;
}
}
}

3
im-uniapp/package.json

@ -4,7 +4,6 @@
"scripts": {}
},
"dependencies": {
"js-audio-recorder": "^1.0.7",
"recorder-core": "^1.3.23122400"
"js-audio-recorder": "^1.0.7"
}
}

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

@ -10,7 +10,7 @@
upper-threshold="200" @scrolltoupper="onScrollToTop"
:scroll-into-view="'chat-item-'+scrollMsgIdx">
<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&&!msgInfo.delete" :headImage="headImage(msgInfo)" @call="onRtCall(msgInfo)"
:showName="showName(msgInfo)" @recall="onRecallMessage" @delete="onDeleteMessage"
@longPressHead="onLongPressHead(msgInfo)" @download="onDownloadFile" :id="'chat-item-'+idx"
:msgInfo="msgInfo" :groupMembers="groupMembers">
@ -544,8 +544,14 @@
});
},
readedMessage() {
if(this.unreadCount == 0){
console.log("0000000000")
return;
}
let url = ""
if (this.chat.type == "GROUP") {
var url = `/message/group/readed?groupId=${this.chat.targetId}`
url = `/message/group/readed?groupId=${this.chat.targetId}`
} else {
url = `/message/private/readed?friendId=${this.chat.targetId}`
}
@ -565,7 +571,6 @@
this.group = group;
this.$store.commit("updateChatFromGroup", group);
this.$store.commit("updateGroup", group);
});
this.$http({
@ -660,8 +665,6 @@
// 20
let size = this.chat.messages.length;
this.showMinIdx = size > 20 ? size - 20 : 0;
//
this.$store.commit("activeChat", options.chatIdx);
//
this.readedMessage()
//
@ -671,6 +674,8 @@
this.loadFriend(this.chat.targetId);
this.loadReaded(this.chat.targetId)
}
//
this.$store.commit("activeChat", options.chatIdx);
//
this.isReceipt = false;
},

42
im-uniapp/pages/chat/chat.vue

@ -10,12 +10,14 @@
温馨提示您现在还没有任何聊天消息快跟您的好友发起聊天吧~
</view>
<scroll-view class="scroll-bar" v-else scroll-with-animation="true" scroll-y="true">
<view v-for="(chat,index) in chatStore.chats" :key="index">
<chat-item :chat="chat" :index="index" @longpress.native="onShowMenu($event,index)"></chat-item>
<view v-for="(chatPos,i) in chatsPos" :key="i">
<chat-item v-if="!chatStore.chats[chatPos.idx].delete" :chat="chatStore.chats[chatPos.idx]"
:active="menu.chatIdx==chatPos.idx" :index="chatPos.idx"
@longpress.native="onShowMenu($event,chatPos.idx)"></chat-item>
</view>
</scroll-view>
<pop-menu v-show="menu.show" :menu-style="menu.style" :items="menu.items" @close="menu.show=false"
<pop-menu v-show="menu.show" :menu-style="menu.style" :items="menu.items" @close="onCloseMenu()"
@select="onSelectMenu"></pop-menu>
</view>
</template>
@ -30,12 +32,12 @@
chatIdx: -1,
items: [{
key: 'DELETE',
name: '删除',
name: '删除该聊天',
icon: 'trash'
},
{
key: 'TOP',
name: '置顶',
name: '置顶该聊天',
icon: 'arrow-up'
}
]
@ -57,6 +59,7 @@
this.menu.show = false;
},
onShowMenu(e, chatIdx) {
this.menu.chatIdx = chatIdx;
uni.getSystemInfo({
success: (res) => {
let touches = e.touches[0];
@ -81,6 +84,10 @@
}
})
},
onCloseMenu() {
this.menu.chatIdx = -1;
this.menu.show = false;
},
removeChat(chatIdx) {
this.$store.commit("removeChat", chatIdx);
},
@ -103,13 +110,30 @@
}
},
computed: {
chatsPos() {
//
let chatsPos = [];
let chats = this.chatStore.chats;
chats.forEach((chat, idx) => {
chatsPos.push({
idx: idx,
sendTime: chat.lastSendTime
})
})
chatsPos.sort((chatPos1, chatPos2) => {
return chatPos2.sendTime - chatPos1.sendTime;
});
return chatsPos;
},
chatStore() {
return this.$store.state.chatStore;
},
unreadCount() {
let count = 0;
this.chatStore.chats.forEach(chat => {
count += chat.unreadCount;
if (!chat.delete) {
count += chat.unreadCount;
}
})
return count;
},
@ -147,10 +171,16 @@
.chat-loading {
display: block;
width: 100%;
height: 100rpx;
background: white;
position: relative;
color: blue;
.loading-box {
position: relative;
}
}
.scroll-bar {

5
im-uniapp/pages/common/user-info.vue

@ -55,8 +55,9 @@
headImage: this.userInfo.headImage,
};
this.$store.commit("openChat", chat);
let chatIdx = this.$store.getters.findChatIdx(chat);
uni.navigateTo({
url:"/pages/chat/chat-box?chatIdx=0"
url:"/pages/chat/chat-box?chatIdx=" + chatIdx
})
},
onAddFriend() {
@ -130,7 +131,7 @@
},
computed: {
isFriend() {
return this.friendInfo != undefined;
return this.friendInfo&&!this.friendInfo.delete;
},
friendInfo(){
let friends = this.$store.state.friendStore.friends;

2
im-uniapp/pages/friend/friend-add.vue

@ -70,7 +70,7 @@
isFriend(userId) {
let friends = this.$store.state.friendStore.friends;
let friend = friends.find((f) => f.id == userId);
return friend != undefined;
return friend&&!friend.delete;
}
}
}

5
im-uniapp/pages/friend/friend-search.vue

@ -5,8 +5,9 @@
</view>
<view class="friend-items">
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true">
<view v-for="(friend,index) in $store.state.friendStore.friends" v-show="searchText && friend.nickName.startsWith(searchText)" :key="index">
<friend-item :friend="friend" :index="index"></friend-item>
<view v-for="(friend,index) in $store.state.friendStore.friends" :key="index">
<friend-item v-if="searchText&&!friend.delete&&friend.nickName.startsWith(searchText)"
:friend="friend" :index="index"></friend-item>
</view>
</scroll-view>
</view>

17
im-uniapp/pages/friend/friend.vue

@ -8,13 +8,18 @@
<uni-icons type="personadd" size="30"></uni-icons>
</view>
</view>
<view class="friend-tip" v-if="$store.state.friendStore.friends.length==0">
<view class="friend-tip" v-if="friends.length==0">
温馨提示您现在还没有任何好友快点击右上方'+'按钮添加好友吧~
</view>
<view class="friend-items" v-else>
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true">
<view v-for="(friend,index) in $store.state.friendStore.friends" :key="index">
<friend-item :friend="friend"></friend-item>
<!-- 先展示在线好友-->
<view v-for="(friend,index) in friends" :key="index">
<friend-item v-if="!friend.delete&&friend.online" :friend="friend"></friend-item>
</view>
<!-- 再展示离线好友-->
<view v-for="(friend,index) in friends" :key="index">
<friend-item v-if="!friend.delete&&!friend.online" :friend="friend"></friend-item>
</view>
</scroll-view>
</view>
@ -40,8 +45,12 @@
url: "/pages/friend/friend-add"
})
}
},
computed:{
friends(){
return this.$store.state.friendStore.friends;
}
}
}
</script>

3
im-uniapp/pages/group/group-info.vue

@ -86,8 +86,9 @@
headImage: this.group.headImage,
};
this.$store.commit("openChat", chat);
let chatIdx = this.$store.getters.findChatIdx(chat);
uni.navigateTo({
url: "/pages/chat/chat-box?chatIdx=0"
url: "/pages/chat/chat-box?chatIdx=" + chatIdx
})
},
onQuitGroup() {

3
im-uniapp/pages/group/group-invite.vue

@ -84,6 +84,9 @@
this.friendItems = [];
let friends = this.$store.state.friendStore.friends;
friends.forEach((f => {
if(f.delete){
return
}
let item = {
id: f.id,
headImage: f.headImage,

77
im-uniapp/store/chatStore.js

@ -3,14 +3,20 @@ import {
MESSAGE_STATUS
} from '@/common/enums.js';
import userStore from './userStore';
/*
uniapp性能优化
1.由于uniapp渲染消息性能非常拉胯,所以先把离线消息存储到cacheChats,
待所有离线消息拉取完成后再统一进行渲染
2.在vuex中对数组进行unshift,splice特别卡所以删除会话会话置顶
除消息等操作进行优化不通过unshift,splice实现改造方案如下
删除会话 通过delete标志判断是否删除
删除消息通过delete标志判断是否删除
会话置顶通过lastSendTime排序确定会话顺序
*/
/* uniapp线cacheChats,
等待所有离线消息拉取完成后再统一进行渲染 */
let cacheChats = [];
export default {
state: {
activeIndex: -1,
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
@ -20,14 +26,19 @@ export default {
mutations: {
initChats(state, chatsData) {
// 暂存至缓冲区
cacheChats = JSON.parse(JSON.stringify(chatsData.chats))
// 只取前10条数据做做样子,一切都为了加快初始化时间
let size = Math.min(chatsData.chats.length,10);
for (let i = 0; i < size; i++) {
let chat = chatsData.chats[i];
chat.messages = [];
state.chats[i] = chat;
cacheChats = [];
for (let chat of chatsData.chats) {
// 已删除的会话直接丢弃
if (chat.delete) {
continue;
}
// 暂存至缓冲区
cacheChats.push(JSON.parse(JSON.stringify(chat)));
// 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间
if (state.chats.length < 15) {
chat.messages = [];
state.chats.push(chat);
}
}
state.privateMsgMaxId = chatsData.privateMsgMaxId || 0;
state.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
@ -48,6 +59,7 @@ export default {
if (chats[idx].type == chatInfo.type &&
chats[idx].targetId === chatInfo.targetId) {
chat = chats[idx];
chat.delete = false;
// 放置头部
this.commit("moveTop", idx)
break;
@ -65,15 +77,15 @@ export default {
unreadCount: 0,
messages: [],
atMe: false,
atAll: false
atAll: false,
delete: false
};
chats.unshift(chat);
chats.push(chat);
this.commit("moveTop", chats.length - 1)
}
this.commit("saveToStorage");
},
activeChat(state, idx) {
let chats = this.getters.findChats();
state.activeIndex = idx;
if (idx >= 0) {
chats[idx].unreadCount = 0;
}
@ -109,7 +121,7 @@ export default {
},
removeChat(state, idx) {
let chats = this.getters.findChats();
chats.splice(idx, 1);
chats[idx].delete = true;
this.commit("saveToStorage");
},
removePrivateChat(state, userId) {
@ -122,17 +134,11 @@ export default {
}
},
moveTop(state, idx) {
// 加载中不移动,防止卡顿
if (this.getters.isLoading()) {
return;
}
let chats = this.getters.findChats();
if (idx > 0) {
let chat = chats[idx];
chats.splice(idx, 1);
chats.unshift(chat);
this.commit("saveToStorage");
}
let chat = chats[idx];
// 最新的时间会显示在顶部
chat.lastSendTime = new Date().getTime();
this.commit("saveToStorage");
},
insertMessage(state, msgInfo) {
// 获取对方id或群id
@ -233,13 +239,13 @@ export default {
for (let idx in chat.messages) {
// 已经发送成功的,根据id删除
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
chat.messages.splice(idx, 1);
chat.messages[idx].delete = true;
break;
}
// 正在发送中的消息可能没有id,根据发送时间删除
if (msgInfo.selfSend && chat.messages[idx].selfSend &&
chat.messages[idx].sendTime == msgInfo.sendTime) {
chat.messages.splice(idx, 1);
chat.messages[idx].delete = true;
break;
}
}
@ -271,24 +277,23 @@ export default {
},
loadingPrivateMsg(state, loadding) {
state.loadingPrivateMsg = loadding;
if (!state.loadingPrivateMsg && !state.loadingGroupMsg) {
if (!this.getters.isLoading()) {
this.commit("refreshChats")
}
},
loadingGroupMsg(state, loadding) {
state.loadingGroupMsg = loadding;
if (!state.loadingPrivateMsg && !state.loadingGroupMsg) {
if (!this.getters.isLoading()) {
this.commit("refreshChats")
}
},
refreshChats(state) {
// 排序
cacheChats.sort((chat1, chat2) => {
return chat2.lastSendTime - chat1.lastSendTime;
});
// 将消息一次性装载回来,只显示前30个会话,多了卡的不行
state.chats = JSON.parse(JSON.stringify(cacheChats.slice(0,30)))
// 将消息一次性装载回来
state.chats = cacheChats;
this.commit("saveToStorage");
},
saveToStorage(state) {
@ -305,12 +310,12 @@ export default {
}
uni.setStorage({
key: key,
data: chatsData
data: chatsData ,
})
},
clear(state) {
cacheChats = [];
state.chats = [];
state.activeIndex = -1;
state.privateMsgMaxId = 0;
state.groupMsgMaxId = 0;
state.loadingPrivateMsg = false;

55
im-uniapp/store/friendStore.js

@ -13,7 +13,7 @@ export default {
},
updateFriend(state, friend) {
state.friends.forEach((f, index) => {
if (f.id == friend.id) {
if (!f.delete && f.id == friend.id) {
// 拷贝属性
let online = state.friends[index].online;
Object.assign(state.friends[index], friend);
@ -22,40 +22,24 @@ export default {
})
},
removeFriend(state, id) {
state.friends.forEach((f, idx) => {
if (f.id == id) {
state.friends.splice(idx, 1)
}
});
let friend = this.getters.findFriend(id);
if(friend){
friend.delete = true;
}
},
addFriend(state, friend) {
state.friends.push(friend);
let f = this.getters.findFriend(friend.id);
if(f){
Object.assign(f, friend);
f.delete = false;
}else{
state.friends.push(friend);
}
},
setOnlineStatus(state, onlineTerminals) {
setOnlineStatus(state, onlineUsers) {
state.friends.forEach((f) => {
let userTerminal = onlineTerminals.find((o) => f.id == o.userId);
if (userTerminal) {
f.online = true;
f.onlineTerminals = userTerminal.terminals;
f.onlineWeb = userTerminal.terminals.indexOf(TERMINAL_TYPE.WEB) >= 0
f.onlineApp = userTerminal.terminals.indexOf(TERMINAL_TYPE.APP) >= 0
} else {
f.online = false;
f.onlineTerminals = [];
f.onlineWeb = false;
f.onlineApp = false;
}
});
state.friends.sort((f1, f2) => {
if (f1.online && !f2.online) {
return -1;
}
if (f2.online && !f1.online) {
return 1;
}
return 0;
let onlineUser = onlineUsers.find((o) => f.id == o.userId);
f.online = !!onlineUser
});
},
refreshOnlineStatus(state) {
@ -67,8 +51,8 @@ export default {
http({
url: '/user/terminal/online?userIds=' + userIds.join(','),
method: 'GET'
}).then((onlineTerminals) => {
this.commit("setOnlineStatus", onlineTerminals);
}).then((onlineUsers) => {
this.commit("setOnlineStatus", onlineUsers);
})
}
// 30s后重新拉取
@ -98,5 +82,10 @@ export default {
})
});
}
},
getters:{
findFriend: (state) => (id) => {
return state.friends.find((f)=>f.id==id);
}
}
}
Loading…
Cancel
Save