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>
  • 加入房间部分

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

<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>
  • 聊天室界面

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

<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>

样式部分

  • 登录和加入房间样式
.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;
    }
  }
}
  • 聊天房间页面样式
.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;
    }
  }
}

脚本部分

引入依赖包和属性设置

  • 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, // 服务端计时间隔
});
  • 用户信息
// 用户信息
const userInfo = reactive({
   
  id: "1", // 用户id
  name: "", // 用户名称
  avatar: "", // 用户头像
  users: [], // 用户在线列表
  usersText: "", // 用户在线列表文本
});
  • 房间信息
// 房间信息
const roomInfo = reactive({
   
  id: 1, // 房间id
  name: "房间1", // 房间名称
  list: ["房间1", "房间2", "房间3"], // 房间列表
  mode: "avatar", // 模式:avatar头像
  scrollTop: 0, // 滚动到顶部距离
  scrollHei: 0, // 滚动高度
  goBottomTimer: null, // 到底部计时
  goBottomTime: 500, // 到顶部计时间隔
});
  • 聊天信息
// 聊天信息
const msg = reactive({
   
  current: "", // 输入框内容
  list: [], // 聊天消息列表
});

实战演练

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

连接 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() 

你可能感兴趣的:(前端框架库,uni-app,vue.js,聊天室,websocket)