那个网站点击率高,制作相册图片合集,wordpress更改ip后无主题,网站建设介绍大全项目背景
当前官网 uni-app vue2 Demo 地址
当前版本功能实现方式较混乱#xff0c;代码逻辑晦涩难懂#xff0c;不利于开发者参考或复用。此实战项目主要优化现有代码结构#xff0c;以确保未来项目的可维护性和扩展性。
重构目标
本次重构中原始 Demo 代码逻辑完全重写…项目背景
当前官网 uni-app vue2 Demo 地址
当前版本功能实现方式较混乱代码逻辑晦涩难懂不利于开发者参考或复用。此实战项目主要优化现有代码结构以确保未来项目的可维护性和扩展性。
重构目标
本次重构中原始 Demo 代码逻辑完全重写原有目录结构以及消息相关组件进行重新调整在 SDK 的调用方式以及实现逻辑上也进行升级调整。此次重构中会话列表、消息列表等数据不再进行本地存储而是远端进行拉取更换设备也可以进行数据获取并在原有功能上增加更多丰富的群组以及消息操作。UI 风格按照最新 UIKit 进行同步更新带来更清爽合理的 Demo 风格。用最新的 SDK 版本恰当的实现清晰的代码展示有价值的功能使用示例。易于将内部的逻辑进行复用后续的功能易于维护增加。将 UI 风格进行统一。uni-app vue2 Demo 仍有一大部分的用户使用可根据实践步骤进行相应升级。
核心重构动作
切换 SDK 引入方式 本地 JS 文件引入通常用于引入项目内的模块、工具函数等而通过 node_modules 引入通常用于引入第三方模块,而 IM SDK 正是三方 js 包并且会持续更新因此这种引入形式更规范且方便一些。 原引入方式
本地引入
import websdk from ../newSDK/Easemob-chat-4.5.0.js;现引入方式
通过node_modules进行导入;
import EaseSDK from easemob-websdk/uniApp/Easemob-chat;统一管理 IM 相关代码 原 uni-app demo 中将 IM SDK 中的实例化统一放在utils/WebIM.js中将配置放在utils/WebConfig.js中SDK 各接口的调用分布在各个组件或页面中因此在后续接口更新或者维护中过于分散如需复用则需要在各个页面中进行查找因此将 IM 相关的调用实例化以及 config 配置新建一个EaseIM文件进行统一管理涉及 IM 相关大部分代码书写至此文件。 项目目录结构EaseIM├── config # IM 相关配置文件├── constant # IM 相关常量文件├── emApis # IM 所有调用 api 集合├── emListener # IM 所有监听回调集合├── utils # IM 相关所需工具方法└── index.js # IM 引入 SDK 以及实例化导出文件
//index.js 示例代码
import EaseSDK from easemob-websdk/uniApp/Easemob-chat;
import { EM_APP_KEY, EM_API_URL, EM_WEB_SOCKET_URL } from ./config;
let EMClient (uni.EMClient {});
EMClient new EaseSDK.connection({appKey: EM_APP_KEY,apiUrl: EM_API_URL,url: EM_WEB_SOCKET_URL,
});
uni.EMClient EMClient;
export { EaseSDK, EMClient };本地数据管理方式变更 在部分用户基于原 uni-app demo 进行二次开发过程中对于 IM 相关的本地数据(消息会话好友列表等)的处理一直是较为头疼的点这种头疼主要体现在进行数据结构调整以及逻辑复用源码阅读时。 这种情况的产生是由于原 demo 中将消息的存储在了 localstorage 当中并且对应的消息存储还进行了一系列的逻辑处理以及封装这种封装还与原始的消息结构完全不同从而导致改动异常困难而原 demo 中的会话列表的实现也是基于 localstorage 中存储的数据形成但环信已经针对会话列表聊天消息好友列表用户属性都进行了远端存储因此调用远端数据进行处理无疑是更简单的方法所以调整后提供良好的示例尤为重要。 由于原始代码篇幅较长不过多的贴源代码进行比较原 demo 本地数据管理核心分别在globaldata、localstorage处于实际场景考虑以及缓存数据存在多页面复用因此将本地缓存的数据管理改用为vuex进行全局状态管理选用vuex作为全局状态管理首先便于将不同的状态数据切分为不同的 module第二数据变更时的调用脉络清晰有利于后续维护以及增加可读性。 下图为改用 vuex 后的 store 的目录结构 下列示例代码以conversation模块为例conversation 的 store 里面主要存储以及管理当前用户的会话列表信息。 import { emConversation, emSilent } from /EaseIM/emApis;
import { EMClient } from /EaseIM;
import { CHAT_TYPE } from /EaseIM/constant;
import Vue from vue;
import MessageStore from ./message;
const {fetchPinConversationFromServer,pinConversationItem,fetchConversationFromServer,removeConversationFromServer,sendChannelAck,
} emConversation();
const {getSilentModeForConversation,getSilentModeForConversationList,setSilentModeForConversation,clearRemindTypeForConversation,
} emSilent();
const ConversationStore {state: {chattingId: , //进入聊天页面聊天中的目标聊天用户信息chattingChatType: CHAT_TYPE.SINGLE_CHAT, //当前聊天页面中的聊天类型类型chattingTypingStatus: false, //当前聊天页面中是否正在输入pinConversationList: [],conversationList: [],silentConversationMap: {},},mutations: {RESET_CONVERSATION_STORE: (state) {},SET_CHATING_USER_INFO: (state, payload) {},SET_CHATING_USER_INFO_TYPING_STATUS: (state, payload) {},SET_PIN_CONVERSATION_LIST: (state, pinConversationList) {},SET_SILENT_CONVERSATION_MAP: (state, payload) {},UPDATE_PIN_CONVERSATION_ITEM: (state, conversationItem) {},SET_CONVERSATION_LIST: (state, conversationList) {},DELETE_CONVERSATION_ITEM: (state, conversationId) {},UPDATE_CONVERSATION_ITEM: (state, payload) {},SET_CONVERSATION_ITEM_READ_SATUS: (state, payload) {},},actions: {//主动更新lastMessageupdateConversationLastMsg: async ({ commit, dispatch }, payload) {},fetchPinConversationList: async ({ commit }) {},fetchConversationList: async ({ commit, dispatch }) {},//处理会话置顶pinConversationItem: async ({ commit }, params) {},deleteConversation: async ({ commit }, params) {},sendConversatonReadedAck: async ({ commit }, params) {},//获取会话免打扰状态fetchSilentConversationList: async ({ commit }, params) {},//获取单个会话免打扰状态fetchSilentConversation: ({ commit }, params) {},//设置会话免打扰setConversationSilentMode: async ({ commit }, params) {},//调整会话已读状态setConversationReadStatus: async ({ commit }, params) {},},getters: {//排序会话列表sortedConversationList(state) {},//排序指定会话列表sortedPinConversationList(state) {},//会话免打扰信息silentConversationMap(state) {},//会话未读总数calcAllUnReadNumFromConversation(state) {},//聊天中用户IDchattingId(state) {},chattingChatType(state) {},chattingTypingStatus(state) {},},
};
export default ConversationStore;可以看到在此 store 文件中有针对会话列表的常见增删改查以及指定会话列表的状态等进行了更为集中的管理在各组件中都可以较为编辑的更新以及使用conversation相关的数据。
下图为原会话列表组件以及获取的代码谁看谁麻。 得益于集中维护且各组件需要编辑复用数据的特性因此将原 demo 逻辑中的涉及全局状态管理的数据以及不同组件会经常使用的数据逻辑全部调整为vuex进行管理且vuex中的数据变更是可以做到响应式的引起视图数据的更新因此充斥在原始 demo 中大量的发布-订阅模式的event bus代码可以进行删除比如典型的这些代码。
//disp.fire 发布事件
//disp.on 订阅事件
//disp.off 卸载订阅
WebIM.conn.open(opt).then(() {//token获取成功即可开始请求用户属性。disp.fire(em.mian.profile.update);disp.fire(em.mian.friendProfile.update);}).catch((err) {console.log(token获取失败, err);});
//监听加好友申请
disp.on(em.subscribe, this.onChatPageSubscribe);
//监听解散群
disp.on(em.invite.deleteGroup, this.onChatPageDeleteGroup);
//监听未读消息数
disp.on(em.unreadspot, this.onChatPageUnreadspot);
//监听未读加群“通知”
disp.on(em.invite.joingroup, this.onChatPageJoingroup);
//监听好友删除
disp.on(em.contacts.remove, this.onChatPageRemoveContacts);
//监听好友关系解除
disp.on(em.unsubscribed, this.onChatPageUnsubscribed);
//页面卸载同步取消onload中的订阅防止重复订阅事件。
disp.off(em.subscribe, this.onChatPageSubscribe);
disp.off(em.invite.deleteGroup, this.onChatPageDeleteGroup);
disp.off(em.unreadspot, this.onChatPageUnreadspot);
disp.off(em.invite.joingroup, this.onChatPageJoingroup);
disp.off(em.contacts.remove, this.onChatPageRemoveContacts);
disp.off(em.unsubscribed, this.onChatPageUnsubscribed);chat 聊天组件重写 chat 聊天组件是指的原项目内的components下的chat组件原 uni-app demo 中 chat 组件的核心作用为实现聊天页面通过在pages中的某个页面级组件引入components/chat后渲染展示其整个聊天页面下面是原引入代码。 template
chat idchat :usernameusername refchat chatTypesingleChat onClickInviteMsgonClickMsg/chat
/templatescript
import chat from ../../components/chat/chat.vue;
export default {data() {return {username: {your: }};},components: {chat},尽管看起来只是简单的引入一个chat组件传入少数参数但实际在二次开发中需要大量修改chat中的逻辑。 由于本身聊天页就属于页面级组件因此在重构后将其“拎到”pages中并进行了逻辑重写和大量的功能增加。 新增功能
会话列表
置顶会话取消置顶会话标记未读会话标记已读会话免打扰会话取消免打扰
新邀请
好友申请操作群组申请操作
群聊
新建群组群组详情页群成员查看操作解散群组转让群组退出群组群内昵称设置消息免打扰群组名称修改群组描述修改群内成员新增或移出群 ID 复制
黑名单
黑名单查看
我的
头像展示昵称展示个性签名展示环信 ID 复制在线状态设置昵称个性签名修改输入状态设置自动通过好友申请设置自动接收群组邀请设置消息免打扰设置隐私入口
聊天页
复制消息回复消息编辑消息删除消息撤回消息
好友详情页
消息免打扰拉黑好友备注
核心代码复用指南 在下面我们将以常见问题的形式指引大家如何去看到自己更加关注的代码逻辑从而帮助我们找到集成中遇到的需要参考示例的代码片段。 如何在已有项目中实现单聊或群聊聊天功能 假设我们只需要在已有项目中只需要实现核心的一个聊天页面功能保障最基本的聊天功能即可我们需要从这个 demo 中 copy 哪些代码就可以实现诉求功能呢 在已有项目中执行来安装所依赖的环信 SDK
npm install easemob-websdk确保你的项目里面也安装了uview-ui库安装方式可以查看uView官网。确保你的项目里面安装z-paging插件。 z-paging一个 uni-app 分页组件。 全平台兼容支持自定义下拉刷新、上拉加载更多支持虚拟列表支持自动管理空数据图、点击返回顶部支持聊天分页、本地分页支持展示最后更新时间支持国际化等等。而在此示例中主要用到了该插件的聊天记录模式来实现下拉加载更多聊天记录。 copy 该示例 Demo 项目中的EaseIM文件至你的项目中建议与你的项目目录中pages平级在EaseIM下的config的index.js文件中请将常量EM_APP_KEY的 appkey 修改为自己的 appkey、copy 该示例项目中的store至你的项目中如果你的项目里面恰好也用到了vuex请自行进行合并。copy 该示例 Demo 项目中pages/emChatContainer并也放入你的项目中的pages目录下不要忘记在pages.json中进行对应页面的配置注册。 {path: pages/emChatContainer/index,style: {navigationStyle: custom,navigationBarTextStyle: white,app-plus: {bounce: none}}},{path: pages/emChatContainer/emSelectUserCard/index,style: {navigationStyle: custom,navigationBarTextStyle: white}},完成上述核心步骤之后我们需要在代码层面完成对 IM 的监听回调挂载以及长连接的建联下面是相关代码演示
挂载监听回调
//App.vue
script
import { EMClient } from /EaseIM;
import { emConnectListener, emMountGlobalListener } from /EaseIM/emListener;
import { emConnect } from /EaseIM/emApis;
import { CONNECT_CALLBACK_TYPE, HANDLER_EVENT_NAME } from /EaseIM/constant;
import { emHandleReconnect } from /EaseIM/utils;
export default {onLaunch() {//传给监听callback回调const connectedCallback (type) {console.log(IM连接回调, type);if (type CONNECT_CALLBACK_TYPE.CONNECT_CALLBACK) {this.onConnectedSuccess();}if (type CONNECT_CALLBACK_TYPE.DISCONNECT_CALLBACK) {this.onDisconnect();}if (type CONNECT_CALLBACK_TYPE.RECONNECTING_CALLBACK) {this.onReconnecting();}};/* 链接所需监听回调 */emConnectListener(connectedCallback);/* 全局类型监听集合、消息、联系人、群组等... */emMountGlobalListener();this.handleAutoLoginEaseIM();},computed: {loginStoreStatus() {return this.$store.state.LoginStore.loginStatus;},loginStoreUserBaseInfos() {return this.$store.state.LoginStore.loginUserBaseInfos;},},methods: {onConnectedSuccess() {const { loginUserId } this.loginStoreUserBaseInfos || {};const finalLoginUserId loginUserId || EMClient.user;if (!this.loginStoreStatus) {this.fetchLoginUserNeedData();uni.hideLoading();console.log(开始跳转至会话列表页面);uni.redirectTo({url: ../home/home?myName finalLoginUserId,});}this.$store.commit(SET_LOGIN_USER_BASE_INFOS, {loginUserId: finalLoginUserId,});this.$store.commit(SET_LOGIN_STATUS, true);},onDisconnect() {const { closeEaseIM } emConnect();const { actionEMReconnect } emHandleReconnect();//断开回调触发后如果业务登录状态为true则说明异常断开需要重新登录if (!this.loginStatus) {uni.showToast({title: 退出登录,icon: none,duration: 2000,});uni.redirectTo({url: ../login/login,});closeEaseIM();} else {console.log(需执行重登逻辑);//执行通过token进行重新登录actionEMReconnect();}},onReconnecting() {uni.showToast({title: IM 重连中...,icon: none,});},//获取登录所需基础参数async fetchLoginUserNeedData() {await this.$store.dispatch(fetchFriendList);//获取好友用户属性await this.$store.dispatch(fetchFriendUserInfoCollection);//获取当前登录用户好友信息await this.$store.dispatch(fetchLoginUserProfile);await this.$store.dispatch(fetchBlockUserList);// 在线状态订阅await this.$store.dispatch(subscribePresenceStatus);this.fetchJoinedGroupList();//初始化缓存本地的新邀请列表this.$store.commit(INIT_RECEIVE_INVITE_LIST);},//获取加入的群组列表async fetchJoinedGroupList() {//获取群组列表await this.$store.dispatch(fetchJoinedGroupList, {isInit: true,});},//自动登录handleAutoLoginEaseIM() {const { actionEMReconnect } emHandleReconnect();const loginInfos uni.getStorageSync(EM_LOGIN_INFOS);if (!loginInfos) return;actionEMReconnect();},},
};
/script接着你需要代码层面实现与环信服务建立连接相关的逻辑相关逻辑类似如下代码。
import { emConnect } from /EaseIM/emApis;
script
import { emConnect } from /EaseIM/emApis;
const { loginWithPassword, loginWithAccessToken } emConnect();
export default {data() {return {/* 环信ID环信密码登录 */loginEaseIMId: ,loginEaseIMPassword: };},methods: {async loginWithUserId() {try {const res await loginWithPassword(this.loginEaseIMId,this.loginEaseIMPassword);this.$store.commit(SET_LOGIN_USER_BASE_INFOS, {loginUserId: this.loginEaseIMId,});this.setEMUserLoginInfosToStorage(this.loginEaseIMId.toLowerCase(),res.accessToken);} catch (error) {console.log(, error);uni.showToast({title: 登录失败,icon: none,});} finally {this.loginEaseIMId ;this.loginEaseIMPassword ;}},setEMUserLoginInfosToStorage(userId, token) {const params {key: EM_LOGIN_INFOS,data: { userId: userId, token: token },};uni.setStorage({ ...params });},},
};
/script在有效建立连接后会自动触发监听回调中的onConnected同时也会触发上述的onConnectedSuccess方法这个表明长连接已经建立可以进行聊天那么接下来你可以点击按钮跳转至emChatContainer组件也可以在onConnected中进行路由跳转成功跳转之后则会如下图页面。
如何在已有项目中集成会话列表功能 如果我们需要在自己的项目中通过引入该项目的会话组件进行二次开发需要复用哪些代码完成一个会话列表界面下面将简述一下需要依赖的代码逻辑。 确保如何在已有项目中实现单聊或群聊聊天功能中的1~7 已完成然后在原项目中将pages/conversation组件按照原始目录 copy 至你的项目并在pages.json完整相关路由注册。修改登录(环信 IM 层面的登录)后的路由跳转至 conversation 页面。默认该组件会在加载时拉取远端环信 conversation 相关数据。接着没有报错的话你将会得到下图的页面。 会话列表以及聊天页面是最常见的一些组件复用需求至于如果你还有其他组件复用疑问或者需求则可以在评论区提出。 重构过程中遇到的问题
使用zpaging组件 该滚动插件在使用聊天记录模式模式时可以追加消息但为了实现编辑消息不得不有一个replace的动作因此不得不在其源码上新增了一个function下面是修改的方法位置。 不推荐这么玩因此如果不需要编辑消息可以忽略下面的修改代码。 //监听编辑消息更新itemonModifyMessage(data) {/*** function updateChatRecordData* changeSrc data-handle.js uni_modules/z-paging/components/z-paging/js/modules* description 此方法为自己往插件库中新增的一个方法主要为更新本地分页数据中已存在的消息体。*/// updateChatRecordData(data) {// if (!this.useChatRecordMode) return;// const _index this.totalData.findIndex((o) o.id data.id);// _index 0 (this.totalData[_index] data);// }this.$refs.paging.updateChatRecordData(data);},会话列表使用 uviewui List 组件无法滚动 页面级滚动与 List 滚动重合冲突因此 home 禁止滚动调整了 List 中的高度使其减去 navbar 以及顶部安全区以及 tabbar 以及底部安全区。 setConversationListHeigth() {uni.getSystemInfo({success: (res) {//顶部安全区高度const statusBarHeight res.statusBarHeight;//底部安全区高度const safeAreaBottom res.safeAreaInsets.bottom;console.log(safeAreaBottom, safeAreaBottom);this.conversationListHeight res.windowHeight -(tabBarHeight navBarHeight searchInputHeight statusBarHeight safeAreaBottom);},});},uviewui u-index-list 制作联系人列表 tabbar 遮挡联系人最后一项 迫不得已最终改了 u-index-list 组件代码 在 customNavHeigth 后面多减了 100 init() {// 设置列表的高度为整个屏幕的高度//减去this.customNavHeight并将this.scrollViewHeight设置为maxHeight//解决当u-index-list组件放在tabbar页面时,scroll-view内容较少时还能滚动this.scrollViewHeight this.sys.windowHeight -this.customNavHeight -this.sys.safeAreaInsets.bottom;if (this.sys.safeAreaInsets.bottom) {this.scrollViewHeight this.scrollViewHeight - 100;} else {this.scrollViewHeight this.scrollViewHeight - 50;}},优化与改进
打包至微信小程序包大小严重超标
要求主包 1.5m 结果代码 6m 因此想了一系列优化手段
static 图片二次压缩原占比 230kb 经过压缩缩减至 73kb 效果明显。运行至小程序时代码压缩uniApp 运行至微信小程序勾选运行时是否压缩代码这个效果很明显直接至 2000kb微信小程序运行时勾选上传代码时自动压缩脚本文件此效果也很明显再次缩减部分大小。确保部分代码条件编译从而时其在非微信小程序平台使用时不被打包。
最后展示的占比图 写在最后
在本次重构后或多或多还有一些技术层面的问题比如聊天页面在一些端上交互效果仍然有待提高还需要后续想办法进行寻求优化而uView2.x也出现了不再进行更新的情况因此如果在进行参考或者复用逻辑进行二次开发的时候也需要将此风险进行评估不过如果在使用中有好的建议以及方案也欢迎进行友好交流。目前该项目的源码还在临时仓库目前还需要进行最后的问题调整以及 READEME.md 编写编写后会交给测试人员进行提测最终版本将会更新至官网 uniApp 相关官方仓库临时仓库地址也会在下方贴出欢迎大家体验并提出意见。
相关链接
升级版本 Github 临时地址当前官网 uni-app vue2 demo 地址官网 uni-app vue3 demo 地址官网 uni-app 包含音视频示例 demo 地址注册环信 IM