持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
之前学习 websocket 基本上都是后端,开几个黑窗口来模拟消息的收发。等到自己真正实战的时候,会发现无从下手,本文会将前后端串联起来,从实战出发,分为上下文,此篇先介绍下前端的 websocket 实现及思路。
前端使用的是 vue ,直接用 <script>
引入的方式。对于前端不是很熟悉的同学(不会一点都不会吧?),可以看下文章中的实现思路,了解下大致的实现思路便可。
前端 websocket
我们先看一个简单的 demo
- 简单 websocket 示例
//用于创建一个webSocket实例,执行后,客户端就会与服务端连接
var ws = new WebSocket("ws://localhost:8080/chat");
//用于指定连接成功后的回调函数
ws.onopen = function(evt) {
console.log("Connection open ...");
//send方法用于向服务器发送数据
ws.send("Hello WebSockets!");
};
//用于指定收到服务器数据后的回调函数
ws.onmessage = function(evt) {
console.log("Received Message: " + evt.data);
ws.close();
};
//用于指定连接关闭后的回调函数
ws.onclose = function(evt) {
console.log("Connection closed.");
};
//用于指定报错时的回调函数
ws.onerror = function (evt) {
console.log(evt.data)
};
websocket的特点:
- 服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种
- 建立在 TCP 协议之上,服务器端的实现比较容易
- 可以发送文本,也可以发送二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是 ws(如果加密,则为wss),服务器网址就是 URL。如ws:localhost:8080/chat
浏览器,F12,可以查看数据收发详情:
实现思路
页面加载完成后,我们通过 new 一个webSocket实例,完成客户端就会与服务端连接,之后我们便可通过这个实例进行消息的收发。
消息的类型支持 文本、表情、图片、语音消息等。
对于文字类型,实现很简单,我们直接调取实例的 send("文本信息")
方法即可发送数据。其实对于其他类型的消息,最终也是转换为文本消息类型。
- 表情:其实就是服务器上保存的图片,发送表情的时候发送的是表情的标识(图片服务器上的地址),对方接受的时候就是通过此地址将表情渲染展示即可
- 图片:实现方式和表情类似,只不过需要把本地的图片先上传到服务器,然后再发送图片标识(图片服务器上的地址)
- 语音:实现方式和图片类似,需要在前端将语音录制然后上传到服务器,然后再发送语音标识(语音服务器上的地址)
- 视频:前端录制视频后上传发送标识;文件:上传本地不同类型的文件后发送文件标识。
消息模板
- 文字:
{userid:1,dstid:2,cate:1,media:1,content:"hello"}
- 图片:
{userid:1,dstid:2,cate:1,media:2,url:"http://www.wekenw.com/a/test.jpg"}
- 语音:
{userid:1,dstid:2,cate:1,media:3,url:"http://www.wekenw.com/a/test.mp3",anount:40}
释义:
- userid : 当前用户id(发送者)
- dstid : 目标用户id(接收者)
- cate : 单聊/群聊
- media : 消息类型
- content : 内容
- url : 资源地址
- anount : 音频时间长度
示例:
//{userid:2,dstid:3,cate:1,media:1,content:"hello"}
var msg = {
userid:1
dstid:2,
cate:1,
media:1,
content:"hello"
};
this.webSocket.send(JSON.stringify(msg))
页面展示
如上文,一条聊天记录是一个 msg 对象,那么展示聊天记录的时候,其实就是把包含 msg 的数组遍历出来,var msglist = [{userid:1,...},{userid:1,...},...]
伪代码:
<div v-for="item in msglist">
<!--如果是对方消息则显示头像(左边)-->
<el-avatar v-if="!item.ismine"></el-avatar>
<!--如果是文字消息,则展示-->
<span v-if="item.msg.media==1" v-text="item.msg.content"></span>
<!--如果是图片/表情消息,则展示-->
<img v-if="item.msg.media==2" :src="item.msg.url">
<!--如果是语音消息,则展示-->
<div v-if="item.msg.media==3">
<img class="audio" src="/asset/img/audiom.png"/>
<audio id="audio4play" src="item.msg.url" style="display: none"></audio>
</div>
<!--如果是我发的消息,则显示头像(右边)-->
<el-avatar v-if="item.ismine"></el-avatar>
</div>
页面组件使用的是 element ,官方地址 element.eleme.cn/#/zh-CN。
代码如下
<div class="chatmessBox" id="convo">
<el-row type="flex" :justify="item.ismine?'end':'star' " :class="item.ismine?'minemess':'' " v-for="item in msglist" :key="item.id">
<div class="flex" v-if="!item.ismine">
<el-avatar icon="el-icon-user-solid"></el-avatar>
<div class="flex-col"><span class="name" v-text="item.account"></span></div>
</div>
<!--文字-->
<div class="messBox" v-if="item.msg.media==1">
<span class="sjicon"></span>
<span v-text="item.msg.content"></span>
</div>
<!--语音-->
<div class="messBox" v-if="item.msg.media==3" @click="playaudio(item.msg.url)">
<img class="audio" src="/asset/img/audiom.png" style="width: 30px;margin: 0 0;"/>
<span v-text="item.msg.amount+'″'"></span>
</div>
<!--表情-->
<img v-if="item.msg.media==4" class="show-emoji" :src="item.msg.url">
<!--图片-->
<el-image
v-if="item.msg.media==5"
class="show-img"
style="width: 100px; height: 100px"
:src="item.msg.url"
:preview-src-list="[item.msg.url]">
</el-image>
<div class="flex" v-if="item.ismine">
<el-avatar icon="el-icon-user-solid"></el-avatar>
<div class="flex-col"><span class="name" v-text="item.account"></span></div>
</div>
</el-row>
</div>
源码
整套源码已上传到云仓库:
- github:github.com/wuchengshi/…
- gitee:gitee.com/wekenw/chat