使用 Node.js 和 Socket.io 构建一个简单的聊天应用程序

2025-06-07

使用 Node.js 和 Socket.io 构建一个简单的聊天应用程序

您是否正在尝试寻找一种构建聊天应用程序的方法,但却难以找到一个可以解释所有内容的简单教程?

这篇文章很适合您!

这是我们将要构建的内容的预览

替代文本

免责声明

本文将介绍聊天背后的逻辑,但不会涉及所有样式部分。如果您需要 CSS 文件,我会在文章末尾提供 GitHub 仓库的链接。

设置

我假设你已经安装了 npm 和 node,并且了解它们的工作原理(至少了解基础知识)。此外,还需要具备 JavaScript 的基础知识。
那么,让我们开始吧。

为应用程序创建一个目录,用您喜欢的编辑器打开该目录:

mkdir chatApplication && cd client

接下来,我们将目录初始化为 Nodejs 应用程序。

npm init -y

现在让我们开始安装构建此应用程序所需的所有依赖项:

npm i express randomcolor socket.io uuid

作为开发依赖项,我们将安装 nodemon,它是一个实用程序,可以监视源中的任何更改并自动重启服务器。

npm i -D nodemon

构建服务器端

我们将服务器端逻辑保存在 app.js 文件(位于主文件夹中)中,这是一个简单的快速服务器

//app.js

const express = require('express');
const app = express();
let randomColor = require('randomcolor');
const uuid = require('uuid');

//middlewares
app.use(express.static('public'));

//routes
app.get('/', (req,res)=>{
    res.sendFile(__dirname + '/client/index.html');
});

//Listen on port 5000
server = app.listen( process.env.PORT || 5000);

现在服务器已经准备好了,让我们开始处理 socket.io 的逻辑。首先,我们需要实例化 socket.io,之后它监听每个连接。当用户连接时,我们的套接字会监听以下事件:

  • change_username:获取用户名并发送
  • new_message:监听用户的新消息
  • 断开连接:监听用户断开聊天连接的情况

因此让我们编写代码来使其工作!

//app.js

//socket.io instantiation
const io = require("socket.io")(server);

const users = [];
const connnections = [];

//listen on every connection
io.on('connection', (socket) => {
    console.log('New user connected');
    //add the new socket to the connections array
    connnections.push(socket)
    //initialize a random color for the socket
    let color = randomColor();

    //Set the first username of the user as 'Anonymous'
    socket.username = 'Anonymous';
    socket.color = color;

    //listen on change_username
    socket.on('change_username', data => {
        let id = uuid.v4(); // create a random id for the user
        socket.id = id;
        socket.username = data.nickName;
        users.push({id, username: socket.username, color: socket.color});
        updateUsernames();
    })

    //update Usernames in the client
    const updateUsernames = () => {
        io.sockets.emit('get users',users)
    }

    //listen on new_message
    socket.on('new_message', (data) => {
        //broadcast the new message
        io.sockets.emit('new_message', {message : data.message, username : socket.username,color: socket.color});
    })

    //Disconnect
    socket.on('disconnect', data => {

        if(!socket.username)
            return;
        //find the user and delete from the users list
        let user = undefined;
        for(let i= 0;i<users.length;i++){
            if(users[i].id === socket.id){
                user = users[i];
                break;
            }
        }
        users.splice(user,1);
        //Update the users list
        updateUsernames();
        connnections.splice(connnections.indexOf(socket),1);
    })
})

如果你想知道randomColor()的作用,你会发现什么时候会覆盖前端

前端

前端非常简单,只包含一个 html 文件和两个 javascript 文件(以及一个 css 文件,本教程不会涉及)。
让我们进入客户端文件夹并创建以下文件:index.htmlchat.jsmodalScript.js以及用于保存style.css 的css 文件夹 。index.html首次渲染时会包含一个模态框获取用户名,至于其他内容,我使用了 flexbox 布局来提高响应速度:

<!--index.html-->

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" const="text/html;charset=UTF-8" />
    <link href="http://fonts.googleapis.com/css?family=Comfortaa" rel="stylesheet" type="text/css">
    <link rel="stylesheet" type="text/css" href="css/style.css" >
    <!--Socket.io scirpt-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
    <title>Simple Chat App</title>
</head>

<body>
    <!-- The Modal -->
    <div id="myModal" class="modal">

        <!-- Modal content -->
        <div class="modal-content">
            <h1 class="modal-title">What's your nickname?</h1>
            <input id="nickname-input" class="custom-input" type="text" />
        </div>

    </div>

    <!--Big wrapper-->
    <div class="big-wrapper">
        <!-- Left Column-->
        <div class="online-user-wrapper">
            <div class="online-user-header-container">
                <header>
                    <h2>Online Users</h2>
                </header>
            </div>
            <div>
                <!--Online users goes here-->
                <ul id="users-list">

                </ul>
            </div>
        </div>
        <!--Chat Wrapper -->
        <div class="chat-wrapper">
            <div class="super-chat-title-container">
                <header>
                    <h1>Chat</h1>
                </header>
            </div>

            <!--Messages container-->
            <div id="chatroom">
                <!--x is typing goes here-->
                <div id="feedback"></div>
            </div>

            <!-- Input zone -->
            <div id="input_zone">
                <input id="message" class="vertical-align custom-input" type="text" />
                <button id="send_message" class="vertical-align btn" type="button">Send</button>
            </div>

        </div>
    </div>
    <!--jQuery script-->
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    <!--Scripts-->
    <script src="./chat.js"></script>
    <script src="./modalScript.js"></script>
</body>
</html>

如果我们保留index.html,那么模式就不会消失,所以我们需要添加一些 javascript 来处理它。

//modalScript.js

// Get the modal
var modal = document.getElementById("myModal");
const nicknameInput = document.getElementById("nickname-input");

// Close modal when nick-name is typed
nicknameInput.onkeypress = e => {
    let keycode = (e.keyCode ? e.keyCode : e.which);
    if(keycode == '13'){
        modal.style.display = "none";
    }
};

如果后端正在监听事件,那么前端必须发送这些事件。我们将通过 chat.js 文件发送这些事件。例如,当新用户进入应用时,我们必须监听该用户,获取用户名并将其发送到后端。

//chat.js file

$(function () {
    //make connection
    let socket = io.connect('http://localhost:5000');

    //buttons and inputs
    let message = $("#message");
    let send_message = $("#send_message");
    let chatroom = $("#chatroom");
    let feedback = $("#feedback");
    let usersList = $("#users-list");
    let nickName = $("#nickname-input");

        //Emit typing
    message.bind("keypress", e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode != '13'){
            socket.emit('typing')
        }
    });

消息

正如您所猜测的消息一样,原理是一样的!

//chat.js

$(function () {
    //make connection
    let socket = io.connect('http://localhost:5000');

    //buttons and inputs
    let message = $("#message");
    let send_message = $("#send_message");
    let chatroom = $("#chatroom");
    let feedback = $("#feedback");
    let usersList = $("#users-list");
    let nickName = $("#nickname-input");

    //Emit message
    // If send message btn is clicked
    send_message.click(function(){
        socket.emit('new_message', {message : message.val()})
    });
    // Or if the enter key is pressed
    message.keypress( e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode == '13'){
            socket.emit('new_message', {message : message.val()})
        }
    })

    //Listen on new_message
    socket.on("new_message", (data) => {
        feedback.html('');
        message.val('');
        //append the new message on the chatroom
        chatroom.append(`
                        <div>
                            <div class="box3 sb14">
                              <p style='color:${data.color}' class="chat-text user-nickname">${data.username}</p>
                              <p class="chat-text" style="color: rgba(0,0,0,0.87)">${data.message}</p>
                            </div>
                        </div>
                        `)
        keepTheChatRoomToTheBottom()
    });

    //Emit a username
    nickName.keypress( e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode == '13'){
            socket.emit('change_username', {nickName : nickName.val()});
            socket.on('get users', data => {
                let html = '';
                for(let i=0;i<data.length;i++){
                    html += `<li class="list-item" style="color: ${data[i].color}">${data[i].username}</li>`;
                }
                usersList.html(html)
            })
        }
    });

});

// function thats keeps the chatbox stick to the bottom
const keepTheChatRoomToTheBottom = () => {
    const chatroom = document.getElementById('chatroom');
    chatroom.scrollTop = chatroom.scrollHeight - chatroom.clientHeight;
}

打字

为了完善我们的聊天应用,我们需要添加一个所有聊天应用都具备的功能:发送正在输入内容的用户信息。实现这个功能非常简单。
chat.js文件中,我们需要在“你”输入时发出消息,并在“其他”用户输入时监听消息。
我们来添加以下几行代码:

    //Emit typing
    message.bind("keypress", e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode != '13'){
            socket.emit('typing')
        }
    });

    //Listen on typing
    socket.on('typing', (data) => {
        feedback.html("<p><i>" + data.username + " is typing a message..." + "</i></p>")
    });

app.js文件中,我们想要广播用户正在输入的内容。只需添加以下三行代码即可:

    //listen on typing
    socket.on('typing', data => {
        socket.broadcast.emit('typing',{username: socket.username})
    })

结论

我们的应用到此就完成了。正如你所见,它非常简单,而且不像我之前想象的那样需要大量的代码。
欢迎随时报告问题和错误,改进代码,让它变得更好。
以下是 Github 仓库:

GitHub 徽标 paolodelia99 /简单节点聊天应用程序

使用 node.js 和 socket.io 构建的简单聊天应用程序

文章来源:https://dev.to/paolodelia99/build-a-simple-chat-app-with-node-js-and-socket-io-apk
PREV
使用 useState 和 useEffect 构建 React Hooks 购物车视频演示文章 Hooks 解释
NEXT
3 个终端命令来提高你的工作效率