• uniapp项目实践总结(十五)使用websocket实现简易聊天室


    导语:在一些社交软件中,经常可以看到各种聊天室的界面,接下来就总结一下聊天室的原理个实现方法,最后做一个简易的聊天室,包括登录/登出、加入/离开房间、发送接收聊天消息等功能。

    目录

    • 准备工作
    • 原理分析
    • 组件实现
    • 实战演练
    • 服务端搭建
    • 案例展示

    准备工作

    • pages/index文件夹下面新建一个名叫chat的组件;
    • 按照前一篇所说的页面结构,编写好预定的聊天页面;

    原理分析

    前端部分

    此聊天室前端方面使用了 uniapp 提供的几个 API 实现包括:

    • uni.connectSocket:连接到 websocket 服务器;
    • SocketTask.onOpen:监听服务端连接打开;
    • SocketTask.onClose:监听服务端连接关闭;
    • SocketTask.onError:监听服务端连接错误;
    • SocketTask.onMessage:监听服务端的消息;
    • SocketTask.send:向服务端发送消息;
    • SocketTask.close:关闭服务端连接;

    后端部分

    此聊天室服务端使用 npm 库ws搭建,另外头像上传部分使用原生node实现,待会儿会详细介绍实现方法。

    组件实现

    准备工作和原理分析完成后,接下来写一个简单的页面,下面主要是展示主要的内容。

    模板部分

    • 登录部分

    包括输入用户名和上传头像的页面和功能。

    <view class="chat-login" v-if="!wsInfo.isLogin">
      <view class="chat-login-item">
        <input
          v-model="userInfo.name"
          class="chat-login-ipt"
          type="text"
          :maxlength="10"
          placeholder="请输入用户名" />
      view>
      <view class="chat-login-item">
        <button class="chat-login-ipt" @click="uploadAvatar">上传头像button>
      view>
      <view class="chat-login-item">
        <button class="chat-login-btn" type="primary" @click="wsLogin">用户登录button>
      view>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 加入房间部分

    包括选择房间的退出登录的页面和功能。

    <view class="chat-login" v-if="wsInfo.isLogin && !wsInfo.isJoin">
      <view class="chat-login-item">
        <picker mode="selector" :range="roomInfo.list" :value="roomInfo.id" @change="changeRoom">
          请选择房间号:{
      {roomInfo.name}}
        picker>
      view>
      <view class="chat-login-item">
        <button class="chat-login-btn" type="primary" @click="joinRoom">进入房间button>
      view>
      <view class="chat-login-item">
        <button type="warn" @click="wsLogout">退出登录button>
      view>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 聊天室界面

    包括展示房间号,退出房间,在线用户列表,聊天消息区域以及输入聊天内容和发送消息的页面和功能。

    <view class="chat-room" v-if="wsInfo.isLogin && wsInfo.isJoin">
      <view class="chat-room-set">
        <text class="chat-room-name">房间{
      { roomInfo.id }}text>
        <button class="chat-room-logout" size="mini" type="warn" @click="leaveRoom">退出房间button>
      view>
      <view class="chat-room-users">
        在线人数({
      {userInfo.users.length}}人):{
      { userInfo.usersText }}view
      >
      <scroll-view
        :scroll-y="true"
        :scroll-top="roomInfo.scrollTop"
        @scroll="handlerScroll"
        class="chat-room-area">
        <view
          :class="{'chat-room-area-item': true, 'active': item.name == userInfo.name}"
          v-for="(item, index) in msg.list"
          :key="`msg${index+1}`">
          <view class="chat-room-user">
            <text
              v-if="roomInfo.mode == 'name'"
              :class="{'chat-room-username': true, 'active': item.name == userInfo.name}"
              >{
      { item.name }}text
            >
            <text v-if="roomInfo.mode == 'name'" class="chat-room-time"> ({
      {item.createTime}}): text>
          view>
          <image
            v-if="roomInfo.mode == 'avatar'"
            class="chat-room-avatar"
            :src="item.name == userInfo.name ? userInfo.avatar : item.avatar">image>
          <view class="chat-room-content"> {
      {item.content}} view>
        view>
        <view id="chat-room-area-pos">view>
      scroll-view>
      <view class="chat-room-bot">
        <input
          class="chat-room-bot-ipt"
          type="text"
          placeholder="请输入内容"
          :maxlength="100"
          v-model="msg.current" />
        <button class="chat-room-bot-btn" size="mini" type="primary" @click="sendMsg">发送button>
      view>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    样式部分

    • 登录和加入房间样式
    .chat-login {
      .chat-login-item {
        margin-bottom: 20rpx;
        height: 80rpx;
        .chat-login-ipt,
        uni-picker {
          box-sizing: border-box;
          padding: 10rpx 30rpx;
          height: 100%;
          font-size: 26rpx;
          border: 1rpx solid $e;
          border-radius: 10rpx;
          color: var(--UI-BG-4);
        }
        uni-picker {
          display: flex;
          align-items: center;
        }
        .chat-login-btn {
          background: $mainColor;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 聊天房间页面样式
    .chat-room {
      display: flex;
      flex-direction: column;
      height: 100%;
      .chat-room-set {
        display: flex;
        justify-content: space-between;
        align-items: center;
        width: 100%;
        font-size: 31rpx;
        font-weight: bold;
        .chat-room-name {
          padding: 10rpx;
        }
        .chat-room-logout {
          margin: 0;
        }
      }
      .chat-room-users {
        margin: 20rpx 0;
        padding: 20rpx 0;
        font-size: 28rpx;
        line-height: 1.5;
        border-bottom: 2rpx solid $e;
        word-break: break-all;
      }
      .chat-room-area {
        box-sizing: border-box;
        padding: 20rpx;
        height: calc(100% - 280rpx);
        background: $f8;
        .chat-room-area-item {
          display: flex;
          flex-direction: row;
          justify-content: flex-start;
          align-items: flex-start;
          margin-bottom: 20rpx;
          padding-bottom: 20rpx;
          .chat-room-user {
            .chat-room-username {
              color: $iptBorColor;
              font-size: 31rpx;
              line-height: 2;
              &.active {
                color: $mainColor;
              }
            }
          }
          .chat-room-avatar {
            width: 68rpx;
            height: 68rpx;
            background: $white;
            border-radius: 10rpx;
          }
          .chat-room-time {
            font-size: 26rpx;
            color: $iptBorColor;
          }
          .chat-room-content {
            box-sizing: border-box;
            margin-left: 12rpx;
            padding: 15rpx;
            max-width: calc(100% - 80rpx);
            text-align: left;
            font-size: 24rpx;
            color: $white;
            border-radius: 10rpx;
            word-break: break-all;
            background: $mainColor;
          }
          &.active {
            flex-direction: row-reverse;
            .chat-room-content {
              margin-left: 0;
              margin-right: 12rpx;
              text-align: right;
              color: $uni-text-color;
              background: $white;
            }
          }
        }
      }
      .chat-room-bot {
        position: fixed;
        bottom: 0;
        left: 0;
        display: flex;
        justify-content: space-between;
        align-items: center;
        box-sizing: border-box;
        padding: 0 30rpx;
        width: 100%;
        height: 100rpx;
        background: $white;
        border-top: 3rpx solid $f8;
        .chat-room-bot-ipt {
          flex: 1;
          box-sizing: border-box;
          padding: 0 30rpx;
          height: 70rpx;
          font-size: 24rpx;
          border: 2rpx solid $iptBorColor;
          border-radius: 10rpx;
        }
        .chat-room-bot-btn {
          margin-left: 50rpx;
          width: 150rpx;
          height: 70rpx;
          line-height: 70rpx;
          font-size: 26rpx;
          color: $white;
          background: $mainColor;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    脚本部分

    引入依赖包和属性设置
    • websocket 信息
    // ws
    let wsInfo = reactive({
       
      ws: null, // ws对象
      alive: false, // 是否连接
      isLogin: false, // 是否登录
      isJoin: false, // 是否加入
      lock: false, // 锁住重连
      reconnectTimer: null, // 重连计时
      reconnectTime: 5000, // 重连计时间隔
      clientTimer: null, // 客户端计时
      clientTime: 10000, // 客户端计时间隔
      serverTimer: null, // 服务端计时
      serverTime: 30000, // 服务端计时间隔
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 用户信息
    // 用户信息
    const userInfo = reactive({
       
      id: "1", // 用户id
      name: "", // 用户名称
      avatar: "", // 用户头像
      users: [], // 用户在线列表
      usersText: "", // 用户在线列表文本
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 房间信息
    // 房间信息
    const roomInfo = reactive({
       
      id: 1, // 房间id
      name: "房间1", // 房间名称
      list: ["房间1", "房间2", "房间3"], // 房间列表
      mode: "avatar", // 模式:avatar头像
      scrollTop: 0, // 滚动到顶部距离
      scrollHei: 0, // 滚动高度
      goBottomTimer: null, // 到底部计时
      goBottomTime: 500, // 到顶部计时间隔
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 聊天信息
    // 聊天信息
    const msg = reactive({
       
      current: "", // 输入框内容
      list: [], // 聊天消息列表
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    实战演练

    基本工作准备完毕后,接下来就开始实现功能。

    连接 websocket

    进入页面后,首先连接上 websocket 服务端。

    • 连接 websocket
    // 连接ws
    function connectWs() {
       
      wsInfo.ws = null;
      wsInfo.ws = uni.connectSocket({
       
        url: proxy.$apis.urls.wsUrl,
        success() {
       
          wsInfo.alive = true;
          console.log("ws连接成功!");
        },
        fail() 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    k8s部署redis集群(6节点,3主3从集群模式)
    心怀远方,顶峰相见!!!
    如何设计缓存中间层
    MyBatis的缓存
    C 和 C++ 中主函数调用其他文件中的函数,实现代码重用
    记录学习--分页时会查两遍数据库吗
    spark安装与入门
    【echart】饼图 legend tooltip格式化比例为0不显示
    7-14 凯撒密码-解密
    为何 DevOps 会给开发人员带来压力和倦怠?
  • 原文地址:https://blog.csdn.net/fed_guanqi/article/details/132865006