Node + Websocket(ws) + Vue3(vite)实现简易聊天室

Node 后端

初始化

npm init -y

安装依赖

npm i ws

创建 index.js

在根目录下创建 index.js 文件

创建好后找到 package.json 文件
在 scripts 里加入启动命令 我们就可以通过 npm run serve 启动服务了

 "scripts": {
    "serve": "node index.js",
  },

编写 websocket 服务端代码

const { WebSocketServer } = require("ws");
const WebSocket = require("ws");

const onlineList = [];

// 我们的 port 是 8888
const wss = new WebSocketServer({ port: 8888 });
// 如果有ws 就表明我们已经初始化成功
if (wss) {
  console.log("websocket Initialized successfully on port: " + 8888);
}

wss.on("connection", function connection(ws, request) {
  let welCome = JSON.stringify({
    type: "tips",
    content: "欢迎加入聊天室,开始畅聊吧~",
  });
  // 初始化时发送欢迎的消息
  ws.send(welCome, { binary: false }); // binary 表示是否是文件

  ws.on("error", console.error);

  // 注册收到消息的事件
  ws.on("message", async function message(data) {
    const message = JSON.parse(data);
    switch (message.type) {
      // 用户在连接成功以后会发送消息 我们会在此获取用户信息
      case "init":
        if (message.userId) {
          // 为当前用户的 ws连接绑定 用户id 用于用户断开链接时 改变用户在线状态
          ws.userId = message.userId;
          // 上线
          keepLatestOnlineList("online", message);
        }
        break;
      // 群发消息
      case "message":
        wss.clients.forEach(function each(client) {
          // readyState、WebSocket.OPEN 他们都是 1 表示当前链接是打开状态
          if (client.readyState === WebSocket.OPEN) {
            client.send(JSON.stringify(message), { binary: false });
          }
        });
        break;
      default:
        break;
    }
  });

  // 被动断开 说明用户已经离开网站了 我们维护在线列表
  ws.on("close", function () {
    keepLatestOnlineList("close", { userId: ws.userId });
  });
});

function keepLatestOnlineList(type, message) {
  let index = onlineList.findIndex((item) => item.userId === message.userId);
  switch (type) {
    // 根据消息类型对消息进行分类处理
    case "online":
      if (index === -1) {
        onlineList.push({
          userId: message.userId,
        });
      }
      console.log(message.nickname + " 上线了...");
      break;
    case "close":
      if (index !== -1) {
        onlineList.splice(index, 1);
        console.log("有用户断开连接...");
      }
      break;
    default:
      break;
  }

  // 群发在线人数
  wss.clients.forEach(function each(client) {
    if (client.readyState === WebSocket.OPEN) {
      let message = JSON.stringify({
        type: "onlineList",
        list: onlineList,
      });
      client.send(message, { binary: false });
    }
  });
}

Vite+Vue3 前端

初始化 vue 项目

npm init vite@latest

初始化好以后 进入项目

cd vite-project

安装依赖

npm i

运行项目

npm run dev

编写组件和样式

需要先把 App 页面的 内容清空

<script setup>
import { ref, onMounted } from "vue";
const userInfo = ref({
  userId: "",
});
const yourMessage = ref("");
const messageList = ref([]);
const onlineList = ref([]);
let ws = null;

const sendMessage = () => {
  if (yourMessage.value) {
    ws.send(
      JSON.stringify({
        type: "message",
        userId: userInfo.value.userId,
        content: yourMessage.value,
      })
    );

    yourMessage.value = "";
  }
};

const initWebsocket = () => {
  ws = new WebSocket("ws://localhost:8888/");
  console.log(ws);

  ws.onopen = () => {
    console.log("websocket连接成功");
    ws.send(
      JSON.stringify({
        type: "init",
        userId: userInfo.value.userId,
        content: "欢迎来到聊天室",
      })
    );
  };

  ws.onerror = () => {
    console.log("websocket连接失败");
  };

  ws.onmessage = (data) => {
    const message = JSON.parse(data.data);
    switch (message.type) {
      case "tips":
        console.log(message.content);
        break;
      case "message":
        messageList.value.push(message);
        break;
      case "onlineList":
        onlineList.value = message.list;
        break;
      default:
        break;
    }
  };

  ws.onclose = () => {
    console.log("websocket连接关闭");
  };
};

onMounted(() => {
  userInfo.value.userId = new Date().getTime().toString().slice(8);
  initWebsocket();
});
</script>

<template>
  <div class="app">
    <div class="chat-container">
      在线人数{{ onlineList.length }}
      <div class="chat-room">
        <div class="message-item" v-for="(item, index) in messageList" :key="index">
          <div class="flex-left" v-if="item.userId !== userInfo.userId">
            <div class="avatar">
              {{ "W" + item.userId }}
            </div>
            <div style="margin-left: 10px" class="message-content">
              {{ item.content }}
            </div>
          </div>
          <div v-else class="flex-right">
            <div class="message-content" style="margin-right: 10px">
              {{ item.content }}
            </div>
            <div class="avatar">
              {{ "W" + item.userId }}
            </div>
          </div>
        </div>
      </div>
      <div class="send-box">
        <input class="message-input" type="text" v-model="yourMessage" @keyup.enter="sendMessage" />
        <button class="send-btn" @click="sendMessage">发送</button>
      </div>
    </div>
  </div>
</template>

<style scoped>
.app {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background-color: #fff;
  display: grid;
  place-items: center;
}

.chat-container {
  width: 600px;
  height: 600px;
  overflow: hidden;
  background: #eae3e3;
  border-radius: 8px;
}

.chat-room {
  height: calc(100% - 100px);
  padding: 10px;
  box-sizing: border-box;
  overflow: auto;
  background: #ecd5d5;
}
.message-item {
  width: 80%;
  margin: 0 auto;
  margin-bottom: 10px;
}

.flex-left {
  display: flex;
  justify-content: flex-start;
}

.flex-right {
  display: flex;
  justify-content: flex-end;
}

.avatar {
  width: 40px;
  height: 40px;
  line-height: 40px;
  border-radius: 50%;
  overflow: hidden;
  background: #fafafa;
}

.message-content {
  min-height: 30px;
  background: #fff;
  border-radius: 8px;
  padding: 10px;
}

.send-box {
  height: 80px;
  display: flex;
  align-items: center;
}

.message-input {
  height: 60px;
  flex: 1;
}

.send-btn {
  width: 150px;
  height: 60px;
  line-height: 60px;
  text-align: center;
}
</style>

云部署

云部署就是将 Websocket 的连接地址改成云服务器的地址 并且带上后缀
比如下面的地址就是带了 ws 后缀的

比如 new WebSocket(“ws://example.com/ws/”);

这样写以后 websocket 访问的是我们的 服务器的 80 端口 我们需要使用 nginx 配置端口映射

ws 应该对应的端口映射是

 server {
    listen 80;
    server_name localhost;
    location /ws/ {
      proxy_pass http://ip地址:8888/; # ip 地址就是服务器地址 也可以改成 域名 8888 就是你的服务器端口号
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade";
      proxy_set_header Host $host;
      proxy_cache_bypass $http_upgrade;
    }
 }

视频教程地址 BiliBili
源码地址 Gitee
小张的聊天室 欢迎来玩

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值