You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js环境下如何在服务端与客户端JS文件间共享MySQL连接

提问内容

我在本站看到过类似问题,但都和我遇到的情况不完全一致,因此发帖提问,希望没有重复已有内容。
我是Node.js与socket.io的新手,目前正在开发一款多聊天室应用,还请大家多多包涵。
我目前卡在代码模块化拆分的步骤,项目目录结构如下:
项目目录结构
我的index.js用于启动express服务器、配置基础socket.io连接,按照我的理解,这个入口index.js文件属于服务端JS。

Index.js

const path = require('path')
const http = require('http')
const express = require('express')
const socketio = require('socket.io')
const { generatemsg } = require('./utils/messages')
const { addUser, removeUser, getUser, getUserInRoom } = require('./utils/users')
const pool = require('./utils/dbconfig');
const db = require('../public/js/database');

const app = express()
const server = http.createServer(app)
const io = socketio(server)
const PORT = process.env.PORT || 3000

const publicdir = path.join(__dirname, '../public')

app.use(express.static(publicdir))

io.on("connection", (socket) => {
    console.log("new connection from socket.io")

    socket.on("join", ({ username, room }, cb) => {

        const { error, user } = addUser({ id: socket.id, username, room })

        if (error) {
            return cb(error)
        }
        
        socket.join(user.room)

        //generate random color
        const colorsArray = ["red", "green", "lime", "purple", "blue"]
        var arrayPos = Math.floor(Math.random() * colorsArray.length)
        var colorTmp = colorsArray[arrayPos]

        //Add New User to DB
        var userTmp = user.username
        var roomTmp = user.room
        
        db.addCurrentUserToDB(userTmp, roomTmp, colorTmp)

        io.to(user.room).emit("roomData", {
            room: user.room,
            users: getUserInRoom(user.room)
        })
        
        cb()
    })

    socket.on("sendMessage", (msg, cb) => {
        const user = getUser(socket.id)
        io.to(user.room).emit("message", generatemsg(user.username, msg))
        cb()
    })

    socket.on("disconnect", () => {
        const user = removeUser(socket.id)
        console.log(user)
        if (user) {
            io.to(user.room).emit("message", generatemsg(`${user.username} left.`))

            io.to(user.room).emit("roomData", {
                room: user.room,
                users: getUserInRoom(user.room)
            })
        }
    })
})


server.listen(PORT, () => {
    console.log("server is up at port: " + PORT)
})

我目前对服务端JS和客户端JS的边界区分比较模糊。我判断index.js是服务端JS的依据是它可以使用变量 = require('模块名')的语法。
utils目录下存放我的数据库配置文件,为了共享MySQL连接,我认为需要创建连接池,这部分的实现代码如下:

dbconfig.js

const mysql = require('mysql');

const pool  = mysql.createPool({
    connectionLimit : 100,
    host: "xxxx",
    port: 3306,
    user: "xxxx",
    password: "xxxx",
    database: 'xxxx',
    multipleStatements: true
});

pool.getConnection(function(err, connection) {
    if (err) throw err; // not connected!
    
    console.log('Connected from dbconfig.js');
});

module.exports.pool = pool;

我不太理解为什么utils文件夹下的文件仍然属于服务端JS,所以我可以在index.js中引入这些文件,同时调用位于客户端database.js文件中的函数。
我把所有会在项目中随时调用的数据库操作函数都存放在public/js文件夹下。

Database.js(精简版)

//Add New User
function addCurrentUserToDB(userTmp, roomTmp, colorTmp) {
var sql = 'INSERT INTO pzs_info (username, roomname, usercolor) VALUES (?, ?, ?)';

pool.getConnection(function(err, connection) {
    if (err) throw err; // not connected!
  
    // Use the connection
    connection.query(sql, [userTmp, roomTmp, colorTmp], (err, result, fields) => {
        if (err) throw err;
    
        console.log('Added user' + userTmp); //result.changedRows
        connection.release();
    });
});
}

现在database.js属于客户端JS,因此即使我在dbconfig.js中导出了MySQL连接,database.js也无法访问该连接,我猜测是因为连接存放在服务端JS文件中。
调整不同版本的代码后,我遇到了各类报错,包括pool不是函数、pool未定义等,我最新尝试的版本代码如下:

Database.js(最新版本)

const dbstuff = require('../../src/utils/dbconfig');

//Add New User
function addCurrentUserToDB(userTmp, roomTmp, colorTmp) {
    var sql = 'INSERT INTO pzs_info (username, roomname, usercolor) VALUES (?, ?, ?)';

    dbstuff.pool.query(sql, [userTmp, roomTmp, colorTmp], function (error, results, fields) {
        if (error) throw error;
        console.log('Added user' + userTmp); //result.changedRows
    });
}

我猜测报错原因是当前database.js属于客户端JS,不支持require函数。
但很奇怪的是,即使控制台输出了database.js无法使用require的报错信息,代码仍然可以正常将用户名、房间号、用户颜色写入数据库,这点我无法理解。
我想我可能完全没有掌握Node.js在客户端与服务端之间共享函数的基础逻辑。

更新:

感谢网友的建议,我引入了esm模块,现在可以混合使用require和import语法,已经可以在database.js(客户端)中引入dbconfig.js(服务端)的连接,这部分已经正常。
但我现在卡在将database.js引入同属客户端的chat.js文件的步骤。我尝试在chat.js顶部添加如下import语句:

import { updateUserList } from './database.js';

但报错SyntaxError: import declarations may only appear at top level of a module。
如果我在chat.html中给script标签添加type="module"属性,如下:

<script src="/js/chat.js" type="module"></script>

又会出现多个报错:chat.js文件404、文件因不允许的mime类型被拦截等。
我现在又陷入了困境,不清楚为什么不能直接在chat.html中引入database.js文件使用,还请大家帮忙解答,谢谢。


解答

核心问题梳理

  • 首先你混淆了服务端JS和客户端JS的边界:只要是运行在Node.js环境中的代码,都属于服务端JS,和它存放的文件夹没有必然关系。你放在public/js下的database.js之所以能成功写入数据库,是因为你在服务端入口index.js中用require引入了它,此时这份代码是在Node环境下执行,调用的是服务端的数据库连接池;而浏览器加载public目录下的database.js时,是在客户端(浏览器)环境运行,既没有require能力,也没有数据库连接权限,所以才会出现客户端控制台报错、但数据库写入成功的现象——报错是浏览器端执行代码抛出的,写入是服务端执行同一份代码完成的,两者互不影响。
  • 其次你有严重的安全风险:public目录是express配置的静态资源目录,所有存放在这里的文件都会直接暴露给任意客户端下载,把数据库配置、操作逻辑放在这里会直接泄露你的数据库账号密码,可能导致数据被篡改、删除。
  • 最后模块导入报错的本质是你走错了方向:数据库操作逻辑本来就不该在客户端运行,不需要让客户端引入database.js。

整改方案

1. 调整文件结构

把public/js/database.js整个移动到src/utils目录下,作为纯服务端模块,不要放在公开的静态资源目录中。

2. 修正逻辑调用规则

所有数据库操作都只在服务端执行:

  • 客户端如果需要触发数据库操作,通过socket.io给服务端发送自定义事件,传递必要参数
  • 服务端监听对应事件,调用utils/database.js中的方法完成数据库操作后,再通过socket.io把结果返回给客户端

3. 前端ES模块报错修复(如果确实需要前端模块化)

如果你的前端逻辑需要用ES模块语法,做两处配置即可:

  • 确保html中script标签的src路径和实际静态资源路径匹配,同时添加type="module"属性
  • 在express的静态资源中间件中补充mime类型配置:
app.use(express.static(publicdir, {
  setHeaders: (res, filePath) => {
    if (filePath.endsWith('.js')) {
      res.setHeader('Content-Type', 'application/javascript')
    }
  }
}))

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.19 04:54:07