当下即时通讯已经成为大多数产品的主要功能,最近在工作过程中需要初步开发一个具有基础通讯功能的客服功能。
根据此开发功能的过程,总结了一些开发经验与方法(使用的SDK不同,实现方法也会有比较大的差异)。
之前从来没有接触过这类功能的开发,不知道内部的功能是不是很复杂。
如果只是实现一个基础版的聊天功能,其实并不复杂,只需要把交互做好,功能基本就可以完成。
复制代码
前提准备
- 成熟的即时通讯服务
- 可调用的 SDK 与通俗易懂的文档
- 可以提供答疑的人员
功能简介
- 可实现文字、图片的发送与接收
- 多人同时在线聊天
- 记录聊天列表与历史记录,支持操作列表
- 支持撤回、下载图片
核心功能
开发流程:SDK 方法登录 -> SDK 方法监听 -> Vuex 管理数据 -> 页面监听数据 -> 展示新消息
消息列表
前两步都是通讯服务提供的 SDK 方法,用于连接服务,建立会话,我们的主要内容在数据的处理与展示上。(如果准备工作不充分,开头两步可能会耗费很多时间~)
数据从监听处到我们的页面展示,数据都需要我们重新组装,因为他们服务的是很多项目,所以不可避免会有很多我们不需要的参数返回,同时还有一些我们自己需要使用的字段可以加进去。有了自己熟悉的数据结构,我们就可以根据结构来判断可以实现的功能。每当 SDK 监听消息的方法触发,数据经过重组存入 Vuex,页面的监听方法察觉到 Vuex 中的数据发生了改变,从而把修改好的数据加入到事先准备好的数组中,渲染到页面上,从而实现实时聊天的第一步,数据展示。但是光有数据展示是不够的,我们在微信使用过程中有几个必要的功能:
1. 下拉加载历史消息
2. 撤回消息
3. 下载图片
4. 聊天列表:消息提示、删除聊天等
5. 离线消息展示
复制代码
以上几条在这次的开发中都有实现,下面我一个个介绍:
下拉加载获取历史消息
对比微信我们不难发现,每次下拉,历史消息都会恰好的停留在顶部下来一条数据的距离,不会使得用户每次下拉消息还得找到刚刚看阅读的位置。
实现理念:通过获取下拉前与下拉后滚动元素的高度差,设置滚动条滚动的距离实现。
关键步骤
// 获取滚动元素的 DOM
const dom = document.getElementById("scrollBox");
// 下拉之前的滚动条高度
const stepHeight = dom.scrollHeight;
// 下拉操作触发接口加载数据
// ...
// 获取加载后元素的滚动条高度
this.$nextTick(() => {
const dom2 = document.getElementById("scrollBox");
dom2.scrollTo(0, dom2.scrollHeight - stepHeight); // 滚动到之前高度的位置即可
})
复制代码
长按撤回
定义一个指令 longPress.js
在页面注册这个指令,然后在展示消息的标签上加入操作方法。
// 注意 因为消息我都单独写成了组件,所以我可以直接调用传入的数据,不需要通过函数入参传导
<div v-longPress="touchinBroker">{{ message.content }}</div>
touchinBroker() {
// 处理数据撤回
this.$emit("setMessage", this.message.id);
}
复制代码
下载图片
前提介绍:通讯服务返回的图片,需要通过 AJAX 中的 GET 方法来获取(需要传入一些参数),获取到的图片是 base64 格式的数据。
本来我打算直接用浏览器右键的下载图片功能,但是我发现下载的是一个 base64 文件,并不是图片,所以还是需要自己写一个下载功能,功能逻辑也比较简单,就是创建一个 canvas,然后把图片用 canvas 展示,再截图下载,代码如下:
export function downloadBase64Img(base64Data) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const MINE_TYPE = "image/png";
const img = new Image();
img.src = base64Data;
img.onload = () => {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0, img.width, img.height);
const imgUrl = canvas.toDataUrl(MINE_TYPE);
const alink = document.createElement("a");
alink.download = "img";
alink.href = imgUrl;
alink.dataset.downloadurl = [MINE_TYPE, alink.download, alink.href].join(":");
document.body.appendChild(alink);
alick.click();
document.body.removeChild(alick);
}
}
复制代码
聊天列表
由于我们使用的 IM 服务是我们公司自己研发的项目,很少项目在使用,所以提供的功能并不完善
聊天列表并不是和我最开始想的那么容易实现。
我当时想不就是一个列表,自己部门做了也没多麻烦。实际上开发过程中,凡是涉及到记录消息的地方都得同步更新列表,所以聊天的过程中有很多地方都需要调用接口去更新。而且我们需要与后端沟通如何设计这个列表的增删改查,耗费了很多时间在开发与测试上。如果SDK可以提供一个客服列表,那代码会简化非常多,页面也会去掉很多逻辑判断,这样就可以花更多的时间在实现辅助功能上,比如发送表情、转发消息等...
离线消息
每次登录后,监听方法都会检测是否有离线消息。利用这个方法,我们在页面设置一个只监听一次的方法,流程类似监听消息的流程。
总结
- 开发前做好功能预研。
- 利用好SDK提供的方法可以节约很多时间。
- 涉及到消息相关的方法,最好让提供服务的一方提供方法,自己去实现很容易事半功倍。
- 如果有关键的功能,服务提供的不符合需求,那么就需要慎重选择是否继续使用该服务,毕竟开发完成后,坑还是得自己填。
- 交互很重要,如果交互没做好,就等于页面充满bug,坑的也是自己。
- 设计人员提供的交互不一定能想的非常完美,可以多借鉴其他的聊天软件,这方面业界的标杆都很牛,可以提供很多思路,开发不应该只想着实现代码,还需要关注用户体验。