Node.js + Socket.io 搭建聊天室应用程序教程

本文将向你展示一个简短的 Socket.io 教程,介绍如何使用 Vanilla JS 前端部分和 Node.js 服务器搭建一个简单的聊天室应用程序。

Socket io 库是一个 JavaScript 库,可在连接的客户端(浏览器)和服务器端之间实现实时、双向、基于事件的通信。它由一个 JavaScript 客户端库和另一个服务器专用库组成。这两个组件共享其大部分 API。

你需要使用默认 package.json 文件创建一个项目。

Node.js + Socket.io 搭建聊天室应用程序教程

创建完成后,需要设置服务器并安装所有需要的依赖项。为此,需要创建一个 index.js 文件并安装 socket.io 和 express。可以使用以下命令:

touch index.js && npm install express socket.io && npm install --save-dev nodemon

在 index.js 中,你需要设置一个本地服务器和基本的 Socket 连接。

基本上,这就是你目前在后台需要做的全部工作。一旦发出正确的请求,连接就会建立。它将通过日志信息显示出来,如下所示:

const express = require("express");
const socket = require("socket.io");

// App setup
const PORT = 5000;
const app = express();
const server = app.listen(PORT, function () {
  console.log(`Listening on port ${PORT}`);
  console.log(`http://localhost:${PORT}`);
});

// Static files
app.use(express.static("public"));

// Socket setup
const io = socket(server);

io.on("connection", function (socket) {
  console.log("Made socket connection");
});

现在,你需要准备前端部分。

首先创建公共文件夹和以下文件:一个名为 index.html 的 html 文件和一个名为 main.js 的 js 文件。您还可以选择添加 style.css。我们还将使用 npm。

下面是 index.html 的脚手架。基本上,你所需要的就是可以参考的 HTML 标记和项目中包含的 Socket.io 脚本。这就是包含 WebSockets 脚本的 index.html 文件的样子。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Socket.io simple chat</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div class="container">
      <div class="inbox">
        <div class="inbox__people">
          <h4>Active users</h4>
        </div>
        <div class="inbox__messages">
          <div class="messages__history"></div>
          <div class="fallback"></div>
        </div>
      </div>

      <form class="message_form">
        <input type="text" class="message_form__input" placeholder="Type a message" />
        <button class="message_form__button" type="submit">
          Enter
        </button>
      </form>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
    <script src="main.js"></script>
  </body>
</html>

然后,需要在前端设置连接。在 main.js 中唯一需要的一行代码是 const socket = io();

页面打开并加载脚本后,连接就建立起来了。接下来要做的是处理新的用户连接、新的消息,最后但并非最不重要的是处理当前正在键入的用户。

在 Socket 中,有两种方式可以发出事件等数据。一种是从用户向所有人(包括用户)发送数据,另一种是向所有其他实例发送事件。我们的想法是显示所有活跃用户的列表。因此,当用户连接时,需要通知用户并获取已激活用户的列表。

const activeUsers = new Set();

io.on("connection", function (socket) {
  console.log("Made socket connection");

  socket.on("new user", function (data) {
    socket.userId = data;
    activeUsers.add(data);
    io.emit("new user", [...activeUsers]);
  });

  socket.on("disconnect", () => {
    activeUsers.delete(socket.userId);
    io.emit("user disconnected", socket.userId);
  });
});

当用户连接时,他们会发出包含用户名信息的事件。应在 Socket 上设置 userId 属性。当用户断开连接时将需要该属性。为此,您需要在 “活动用户集合 “中添加一个用户名,并发出一个包含所有活动用户列表的事件。

我在 main.js 中创建了两个函数和两个 Socket 监听器。

const socket = io();

const inboxPeople = document.querySelector(".inbox__people");

let userName = "";

const newUserConnected = (user) => {
  userName = user || `User${Math.floor(Math.random() * 1000000)}`;
  socket.emit("new user", userName);
  addToUsersBox(userName);
};

const addToUsersBox = (userName) => {
  if (!!document.querySelector(`.${userName}-userlist`)) {
    return;
  }

  const userBox = `
    <div class="chat_ib ${userName}-userlist">
      <h5>${userName}</h5>
    </div>
  `;
  inboxPeople.innerHTML += userBox;
};

// new user is created so we generate nickname and emit event
newUserConnected();

socket.on("new user", function (data) {
  data.map((user) => addToUsersBox(user));
});

socket.on("user disconnected", function (userName) {
  document.querySelector(`.${userName}-userlist`).remove();
});

首先,我创建了一个新用户(可以通过添加一些真实用户名的提示来轻松扩展功能),并以该用户名发出一个事件。此外,我还将其添加到侧边栏的所有活跃用户中。当用户在服务器端断开连接时,他们就会从 “活动用户集合 “中移除,并发出包含其用户名的断开连接事件。之后,他们会从客户端的边栏中移除。

既然我们已经处理了用户连接和断开连接的方式,现在是处理新消息的时候了。

在服务器端,只需添加下面所示的监听器即可。

socket.on("chat message", function (data) {
    io.emit("chat message", data);
});

在客户端,还需要做一些事情,如下所示:

const inputField = document.querySelector(".message_form__input");
const messageForm = document.querySelector(".message_form");
const messageBox = document.querySelector(".messages__history");

const addNewMessage = ({ user, message }) => {
  const time = new Date();
  const formattedTime = time.toLocaleString("en-US", { hour: "numeric", minute: "numeric" });

  const receivedMsg = `
  <div class="incoming__message">
    <div class="received__message">
      <p>${message}</p>
      <div class="message__info">
        <span class="message__author">${user}</span>
        <span class="time_date">${formattedTime}</span>
      </div>
    </div>
  </div>`;

  const myMsg = `
  <div class="outgoing__message">
    <div class="sent__message">
      <p>${message}</p>
      <div class="message__info">
        <span class="time_date">${formattedTime}</span>
      </div>
    </div>
  </div>`;

  messageBox.innerHTML += user === userName ? myMsg : receivedMsg;
};

messageForm.addEventListener("submit", (e) => {
  e.preventDefault();
  if (!inputField.value) {
    return;
  }

  socket.emit("chat message", {
    message: inputField.value,
    nick: userName,
  });

  inputField.value = "";
});

socket.on("chat message", function (data) {
  addNewMessage({ user: data.nick, message: data.message });
});

从 socket 接收到信息后,会立即触发负责显示新信息的函数。信息会通过表单提交的监听器函数推送到服务器。只需检查客户端是否为消息作者即可。

现在,我将向你展示如何发出负责通知除你的连接之外的所有其他连接的事件。我们希望显示用户正在输入的信息。

在服务器端,需要添加 socket.broadcast.emit。请看下面(这是文件的最终版本):

const express = require("express");
const socket = require("socket.io");

// App setup
const PORT = 5000;
const app = express();
const server = app.listen(PORT, function () {
  console.log(`Listening on port ${PORT}`);
  console.log(`http://localhost:${PORT}`);
});

// Static files
app.use(express.static("public"));

// Socket setup
const io = socket(server);

const activeUsers = new Set();

io.on("connection", function (socket) {
  console.log("Made socket connection");

  socket.on("new user", function (data) {
    socket.userId = data;
    activeUsers.add(data);
    io.emit("new user", [...activeUsers]);
  });

  socket.on("disconnect", () => {
    activeUsers.delete(socket.userId);
    io.emit("user disconnected", socket.userId);
  });

  socket.on("chat message", function (data) {
    io.emit("chat message", data);
  });
  
  socket.on("typing", function (data) {
    socket.broadcast.emit("typing", data);
  });
});

在客户端,您需要查看输入字段按键事件监听器和键入时的套接字监听器。下面是这两个事件和 main.js 的最终版本:

const socket = io();

const inboxPeople = document.querySelector(".inbox__people");
const inputField = document.querySelector(".message_form__input");
const messageForm = document.querySelector(".message_form");
const messageBox = document.querySelector(".messages__history");
const fallback = document.querySelector(".fallback");

let userName = "";

const newUserConnected = (user) => {
  userName = user || `User${Math.floor(Math.random() * 1000000)}`;
  socket.emit("new user", userName);
  addToUsersBox(userName);
};

const addToUsersBox = (userName) => {
  if (!!document.querySelector(`.${userName}-userlist`)) {
    return;
  }

  const userBox = `
    <div class="chat_ib ${userName}-userlist">
      <h5>${userName}</h5>
    </div>
  `;
  inboxPeople.innerHTML += userBox;
};

const addNewMessage = ({ user, message }) => {
  const time = new Date();
  const formattedTime = time.toLocaleString("en-US", { hour: "numeric", minute: "numeric" });

  const receivedMsg = `
  <div class="incoming__message">
    <div class="received__message">
      <p>${message}</p>
      <div class="message__info">
        <span class="message__author">${user}</span>
        <span class="time_date">${formattedTime}</span>
      </div>
    </div>
  </div>`;

  const myMsg = `
  <div class="outgoing__message">
    <div class="sent__message">
      <p>${message}</p>
      <div class="message__info">
        <span class="time_date">${formattedTime}</span>
      </div>
    </div>
  </div>`;

  messageBox.innerHTML += user === userName ? myMsg : receivedMsg;
};

// new user is created so we generate nickname and emit event
newUserConnected();

messageForm.addEventListener("submit", (e) => {
  e.preventDefault();
  if (!inputField.value) {
    return;
  }

  socket.emit("chat message", {
    message: inputField.value,
    nick: userName,
  });

  inputField.value = "";
});

inputField.addEventListener("keyup", () => {
  socket.emit("typing", {
    isTyping: inputField.value.length > 0,
    nick: userName,
  });
});

socket.on("new user", function (data) {
  data.map((user) => addToUsersBox(user));
});

socket.on("user disconnected", function (userName) {
  document.querySelector(`.${userName}-userlist`).remove();
});

socket.on("chat message", function (data) {
  addNewMessage({ user: data.nick, message: data.message });
});


socket.on("typing", function (data) {
  const { isTyping, nick } = data;

  if (!isTyping) {
    fallback.innerHTML = "";
    return;
  }

  fallback.innerHTML = `<p>${nick} is typing...</p>`;
});

正如你所看到的,使用 Socket.io 构建一个具备基本聊天信息功能的聊天应用程序(或其他任何网络应用程序)并不困难。我希望这篇简短的 Socket.io 教程能帮助您了解使用这种方法有多简单,以及使用 JavaScript 或 TypeScript 和一些基本的 Node.js 知识可以做多少有趣的事情。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论