原文:https://juejin.cn/post/7337114587123335180?searchId=20240509192958AF7D129567F92AD7E083
公众号:程序员白特,欢迎一起交流学习~
前言
我记得2021年的时候做过聊天功能,那时业务也只限微信小程序
那时候的心路历程是:
❝
卧槽,让我写一个聊天功能这么高大上??
嗯?这么简单,不就画画页面来个轮询吗,加个websocket也还行吧
然后,卧槽?这查看历史聊天记录什么鬼,页面闪一下不太好啊,真的能做到微信的那种效果吗
❞
然后一堆调研加测试,总算在小程序中查看历史记录没那么鬼畜了,但是总是感觉不是最佳解决方案。
❝
那时打出的子弹,一直等到现在击中了我
❞
最近又回想到了这个痛点,于是网上想看看有没有大佬发解决方案,结果还真被我找到了。
正文开始
1,效果展示
上才艺~~~
2,聊天页面
2.1,查看历史聊天记录的坑
常规写法加载历史记录拼接到聊天主体的顶部后,滚动条会回到顶部、不在原聊天页面
直接上图
而我们以往的解决方案也只是各种利用缓存
、scroll的滚动定位
把回到顶部的滚动条重新拉回加载历史记录前的位置,好让我们可以继续在原聊天页面。
但即使我们做了很多优化,也会有安卓和苹果部分机型适配问题,还是不自然,可能会出现页面闪动
。
其实吧,解决方案只有两行css代码
\~~~
2.2,解决方案:flex神功
想优雅顺滑的在聊天框里查看历史记录,这两行css代码
就是flex的这个翻转属性
dispaly:flex;
flex-direction: column-reverse
「灵感来源~~~」
小伙伴可以看到,在加载更多数据时
❝
滚动条位置没变、加载数据后还是原聊天页面的位置
❞
这不就是我们之前的痛点吗~~~
所以,我们只需要翻转位置,用这个就可以优雅流畅的实现微信的加载历史记录啦
flex-direction: column-reverse
官方的意思:指定Flex容器中子元素的排列方向为列(从上到下),并且将其顺序反转(从底部到顶部)
如果感觉还是抽象,不好理解的话,那就直接上图,不加column-reverse
的样子
加了column-reverse
的样子
至此,我们用column-reverse
再搭配data数据的位置处理
就完美解决加载历史记录的历史性问题啦
代码放最后啦~~~
2.3,其他问题
2.3.1,数据过少时第一屏展示
因为用了翻转,数据少的时候会出现上图的问题
只需要.mainArea
加上height:100%
然后额外写个适配盒子就行
flex-grow: 1;
flex-shrink: 1;
2.3.2,用了scroll-view导致的问题
这一part是因为我用了uniapp
里 scroll-view
组件导致的坑以及解决方案,小伙伴们没用这个组件的**「可忽略~~~」**
如下图,.mainArea
使用了height:100%
后,继承了父级高度后scroll-view滚动条消失了。
.mainArea
去掉height:100%
后scroll-view滚动条出现,但是第一屏数据过多时不会滚动到底部展示最新信息
解决方案:第一屏手动进行滚动条置顶
scrollBottom() {
if (this.firstLoad) return;
// 第一屏后不触发
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query
.select("#mainArea")
.boundingClientRect((data) => {
console.log(data);
if (data.height > +this.chatHeight) {
this.scrollTop = data.height; // 填写个较大的数
this.firstLoad = true;
}
})
.exec();
});
},
3,服务端
使用koa自己搭一个websocket服务端
3.1 服务端项目目录
package.json
{
"name": "websocketapi",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"koa": "^2.14.2",
"koa-router": "^12.0.1",
"koa-websocket": "^7.0.0"
}
}
koa-tcp.js
const koa = require('koa')
const Router = require('koa-router')
const ws = require('koa-websocket')
const app = ws(new koa())
const router = new Router()
/**
* 服务端给客户端的聊天信息格式
* {
id: lastid,
showTime: 是否展示时间,
time: nowDate,
type: type,
userinfo: {
uid: this.myuid,
username: this.username,
face: this.avatar,
},
content: {
url:'',
text:'',
w:'',
h:''
},
}
消息数据队列的队头为最新消息,以次往下为老消息
客户端展示需要reverse(): 客户端聊天窗口最下面需要为最新消息,所以队列尾部为最新消息,以此往上为老消息
*/
router.all('/websocket/:id', async (ctx) => {
// const query = ctx.query
console.log(JSON.stringify(ctx.params))
ctx.websocket.send('我是小服,告诉你连接成功啦')
ctx.websocket.on('message', (res) => {
console.log(`服务端收到消息, ${res}`)
let data = JSON.parse(res)
if (data.type === 'chat') {
ctx.websocket.send(`我也会说${data.text}`)
}
})
ctx.websocket.on('close', () => {
console.log('服务端关闭')
})
})
// 将路由中间件添加到Koa应用中
app.ws.use(router.routes()).use(router.allowedMethods())
app.listen(9001, () => {
console.log('socket is connect')
})
切到server目录
下yarn
然后执行nodemon koa-tcp.js
没有nodemon
的小伙伴要装一下
「代码区」
完整项目Github传送门
聊天页面的核心代码如下(包含data数据的位置处理和与服务端联动)
https://code.juejin.cn/pen/7337211042621521959(跳转链接或者点击下方阅读原文查看)