1. 核心功能:发送/接收消息、用户列表、在线状态、消息历史。
  2. 技术栈
    • 前端: HTML, CSS, jQuery
    • 后端: Python (使用 Flask 框架,因为它轻量且易于上手)
    • 实时通信: WebSocket (使用 Flask-SocketIO 库,它封装了 WebSocket,简化了实现)
  3. 代码结构:清晰的前后端分离代码。
  4. 详细步骤:从环境搭建到运行测试的全过程。

为什么选择这个技术栈?

  • jQuery: 虽然现在主流是 Vue/React,但 jQuery 在处理 DOM、事件绑定、AJAX 请求方面依然非常高效和简洁,非常适合快速构建此类交互应用。
  • Python + Flask: Python 语法简洁,Flask 是一个“微框架”,不会给你带来太多束缚,让你能专注于核心业务逻辑。
  • WebSocket: 这是实现实时聊天室的关键,传统的 HTTP 轮询(Polling)效率低下,而 WebSocket 允许服务器主动向客户端推送消息,延迟极低,体验流畅。

第 1 步:环境准备

你需要安装 Python 和 pip,安装必要的 Python 库:

jquery网页在线聊天室
(图片来源网络,侵删)
pip install flask flask-socketio python-socketio eventlet
  • flask: Web 框架。
  • flask-socketio: Flask 的 SocketIO 扩展。
  • python-socketio: SocketIO 的核心库。
  • eventlet: 一个高性能的 Python 异步网络库,SocketIO 推荐使用它作为 WSGI 服务器。

第 2 步:后端代码 (Python Flask-SocketIO)

创建一个名为 app.py 的文件,这是聊天室的大脑。

# app.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
# 创建 Flask 应用实例
app = Flask(__name__)
# 设置一个密钥,用于 session 加密
app.config['SECRET_KEY'] = 'your-secret-key!'
# 创建 SocketIO 实例,并传入 Flask 应用
socketio = SocketIO(app, cors_allowed_origins="*")
# 存储在线用户和聊天历史的字典
users = {}
chat_history = []
# 当客户端连接时触发
@socketio.on('connect')
def handle_connect():
    print('Client connected')
    # 将聊天历史发送给新连接的客户端
    emit('load_history', chat_history)
# 当客户端断开连接时触发
@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')
    # 从用户列表中移除断开连接的用户
    if request.sid in users:
        username = users[request.sid]
        del users[request.sid]
        # 广播用户下线消息
        emit('user_list', list(users.values()), broadcast=True)
        emit('message', {
            'username': 'System',
            'text': f'{username} has left the chat.',
            'is_system': True
        }, broadcast=True)
# 当客户端发送 'message' 事件时触发
@socketio.on('message')
def handle_message(data):
    # data 是一个字典,包含 'username' 和 'text'
    username = data['username']
    text = data['text']
    # 创建消息字典
    message = {
        'username': username,
        'text': text,
        'timestamp': data.get('timestamp') # 可以在前端生成时间戳
    }
    # 将消息添加到聊天历史
    chat_history.append(message)
    # 广播消息给所有连接的客户端
    emit('message', message, broadcast=True)
# 当客户端发送 'join' 事件时触发(用户加入)
@socketio.on('join')
def handle_join(data):
    username = data['username']
    # 将用户名与当前会话ID (session id) 关联
    users[request.sid] = username
    # 广播新的用户列表给所有客户端
    emit('user_list', list(users.values()), broadcast=True)
    # 广播用户加入消息
    join_message = {
        'username': 'System',
        'text': f'{username} has joined the chat.',
        'is_system': True
    }
    emit('message', join_message, broadcast=True)
# 定义路由,当用户访问根路径时,渲染聊天室页面
@app.route('/')
def index():
    return render_template('index.html')
# 主程序入口
if __name__ == '__main__':
    # 使用 eventlet 作为 WSGI 服务器运行应用
    socketio.run(app, debug=True, host='0.0.0.0', port=5000)

第 3 步:前端代码 (HTML, CSS, jQuery)

在你的项目目录下,创建一个 templates 文件夹,然后在里面创建 index.html 文件。

your_project/
├── app.py
└── templates/
    └── index.html
    └── style.css

templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">jQuery Chat Room</title>
    <!-- 引入 jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <!-- 引入 Socket.IO 客户端库 -->
    <script src="https://cdn.socket.io/4.5.0/socket.io.min.js"></script>
    <!-- 引入自定义样式 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="chat-container">
        <div class="chat-header">
            <h1>jQuery Chat Room</h1>
        </div>
        <div class="chat-main">
            <div class="chat-sidebar">
                <h2>Online Users</h2>
                <ul id="user-list">
                    <!-- 用户列表将通过 jQuery 动态添加 -->
                </ul>
            </div>
            <div class="chat-messages">
                <div id="message-container">
                    <!-- 消息将通过 jQuery 动态添加 -->
                </div>
                <div class="chat-input-area">
                    <input type="text" id="username-input" placeholder="Enter your name">
                    <button id="join-btn">Join</button>
                </div>
                <div class="chat-input-area hidden" id="chat-area">
                    <input type="text" id="message-input" placeholder="Type a message...">
                    <button id="send-btn">Send</button>
                </div>
            </div>
        </div>
    </div>
    <script>
        // 连接到后端的 SocketIO 服务器
        const socket = io();
        // DOM 元素
        const $messageContainer = $('#message-container');
        const $messageInput = $('#message-input');
        const $sendBtn = $('#send-btn');
        const $userList = $('#user-list');
        const $usernameInput = $('#username-input');
        const $joinBtn = $('#join-btn');
        const $chatArea = $('#chat-area');
        const $chatInputArea = $('.chat-input-area');
        // 发送消息
        function sendMessage() {
            const text = $messageInput.val().trim();
            if (text) {
                const username = $usernameInput.val().trim();
                const timestamp = new Date().toLocaleTimeString();
                // 发送 'message' 事件到服务器
                socket.emit('message', { username, text, timestamp });
                $messageInput.val(''); // 清空输入框
            }
        }
        // 加入聊天室
        function joinChat() {
            const username = $usernameInput.val().trim();
            if (username) {
                // 发送 'join' 事件到服务器
                socket.emit('join', { username });
                $usernameInput.prop('disabled', true);
                $joinBtn.hide();
                $chatArea.removeClass('hidden');
            }
        }
        // 绑定发送按钮点击事件
        $sendBtn.on('click', sendMessage);
        // 绑定回车键发送消息
        $messageInput.on('keypress', function(e) {
            if (e.which === 13) { // 13 是回车键的 keyCode
                sendMessage();
            }
        });
        // 绑定加入按钮点击事件
        $joinBtn.on('click', joinChat);
        // 监听服务器发送的 'message' 事件
        socket.on('message', function(data) {
            // 如果是系统消息,样式不同
            const messageClass = data.is_system ? 'system-message' : 'user-message';
            const usernameDisplay = data.is_system ? '' : `<strong>${data.username}:</strong> `;
            const messageHtml = `
                <div class="message ${messageClass}">
                    <span class="timestamp">${data.timestamp}</span>
                    <span class="text">${usernameDisplay}${data.text}</span>
                </div>
            `;
            $messageContainer.append(messageHtml);
            // 自动滚动到底部
            $messageContainer.scrollTop($messageContainer[0].scrollHeight);
        });
        // 监听服务器发送的 'user_list' 事件
        socket.on('user_list', function(users) {
            $userList.empty(); // 清空现有列表
            users.forEach(user => {
                $userList.append(`<li>${user}</li>`);
            });
        });
        // 监听服务器发送的 'load_history' 事件
        socket.on('load_history', function(history) {
            $messageContainer.empty();
            history.forEach(data => {
                const messageClass = data.is_system ? 'system-message' : 'user-message';
                const usernameDisplay = data.is_system ? '' : `<strong>${data.username}:</strong> `;
                const messageHtml = `
                    <div class="message ${messageClass}">
                        <span class="timestamp">${data.timestamp}</span>
                        <span class="text">${usernameDisplay}${data.text}</span>
                    </div>
                `;
                $messageContainer.append(messageHtml);
            });
            $messageContainer.scrollTop($messageContainer[0].scrollHeight);
        });
    </script>
</body>
</html>

static/style.css

static 文件夹下创建 style.css 文件。

/* static/style.css */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    margin: 0;
    padding: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}
.chat-container {
    width: 90%;
    max-width: 900px;
    background: #ffffff;
    border-radius: 10px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    height: 80vh;
}
.chat-header {
    background: #007bff;
    color: white;
    padding: 15px 20px;
    text-align: center;
}
.chat-main {
    display: flex;
    flex: 1;
    overflow: hidden;
}
.chat-sidebar {
    width: 200px;
    background: #f8f9fa;
    border-right: 1px solid #dee2e6;
    padding: 15px;
    overflow-y: auto;
}
.chat-sidebar h2 {
    margin-top: 0;
    font-size: 1.1em;
    color: #495057;
}
#user-list {
    list-style: none;
    padding: 0;
    margin: 10px 0 0 0;
}
#user-list li {
    padding: 8px;
    background: #e9ecef;
    margin-bottom: 5px;
    border-radius: 5px;
    text-align: center;
}
.chat-messages {
    flex: 1;
    display: flex;
    flex-direction: column;
}
#message-container {
    flex: 1;
    padding: 20px;
    overflow-y: auto;
    background: white;
}
.message {
    margin-bottom: 15px;
    padding: 10px 15px;
    border-radius: 18px;
    max-width: 70%;
    word-wrap: break-word;
}
.user-message {
    background: #e3f2fd;
    align-self: flex-end;
    text-align: right;
    border-bottom-right-radius: 5px;
}
.system-message {
    background: #f1f1f1;
    text-align: center;
    font-style: italic;
    color: #6c757d;
    max-width: 100%;
    margin: 10px auto;
}
.message .timestamp {
    font-size: 0.75em;
    color: #6c757d;
    margin-right: 8px;
}
.message .text {
    display: block;
}
.chat-input-area {
    display: flex;
    padding: 15px;
    border-top: 1px solid #dee2e6;
    background: #f8f9fa;
}
.chat-input-area.hidden {
    display: none;
}
.chat-input-area input[type="text"] {
    flex: 1;
    padding: 10px 15px;
    border: 1px solid #ced4da;
    border-radius: 20px;
    outline: none;
    font-size: 1em;
}
.chat-input-area button {
    margin-left: 10px;
    padding: 10px 20px;
    border: none;
    border-radius: 20px;
    background: #007bff;
    color: white;
    cursor: pointer;
    font-size: 1em;
    transition: background-color 0.2s;
}
.chat-input-area button:hover {
    background: #0056b3;
}

第 4 步:运行和测试

  1. 确保目录结构正确

    jquery网页在线聊天室
    (图片来源网络,侵删)
    my_chat_app/
    ├── app.py
    ├── static/
    │   └── style.css
    └── templates/
        └── index.html
  2. 启动服务器: 在你的项目根目录(my_chat_app/)下,打开终端或命令提示符,运行:

    python app.py

    你应该会看到类似以下的输出:

     * Serving Flask app 'app'
     * Debug mode: on
     * Running on all addresses (0.0.0.0)
     * Running on http://127.0.0.1:5000
     * Running on http://192.168.x.x:5000
  3. 打开浏览器测试

    • 打开两个或更多的浏览器窗口(或标签页)。
    • 在每个窗口中,访问 http://127.0.0.1:5000
    • 在第一个窗口中输入一个用户名,点击 "Join"。
    • 你会看到 "用户列表" 中出现了你的用户名,并且消息区域显示了一条 "XXX has joined the chat" 的系统消息。
    • 在第二个窗口中输入另一个不同的用户名,点击 "Join"。
    • 两个用户都应该出现在彼此的 "用户列表" 中,并且都收到了新用户加入的系统消息。
    • 在任意一个窗口的输入框中输入消息,点击 "Send" 或按回车。
    • 你会看到这条消息立即出现在所有窗口的消息区域中。
    • 关闭任意一个浏览器窗口,其他窗口会立即收到 "XXX has left the chat" 的系统消息,并且该用户会从 "用户列表" 中消失。
  • 实时消息:使用 WebSocket 实现了毫秒级的消息传递。
  • 用户管理:用户可以加入,系统会广播更新用户列表和加入/离开消息。
  • 聊天历史:新用户加入时会自动加载之前的聊天记录。
  • 响应式设计:使用 Flexbox 布局,界面在不同屏幕尺寸下表现良好。
  • 用户体验:支持回车发送、自动滚动、区分普通消息和系统消息。

这个项目是一个非常好的起点,你可以在此基础上继续扩展,比如添加私聊功能、文件发送、消息持久化到数据库等。

jquery网页在线聊天室
(图片来源网络,侵删)