Discord.js+Express跨文件传递client变量报错如何解决
问题原因
报错是CommonJS模块循环依赖+导出/导入顺序错误导致的,和client是否完成登录初始化无关:
- 你在
index.js中创建client实例后,先执行require('./routes.js')加载路由 - 路由文件加载时会反向
require('./index.js'),但此时index.js还没运行到module.exports = { client }这一行,路由文件拿到的导出对象是空值,最终传入vote.js的client是undefined - 读取
undefined.users.cache自然会抛出类型错误。
另外你直接读client.users.cache也有隐患:如果目标用户不在本地缓存里,哪怕client实例正常,也会拿到undefined导致发消息失败。
修复方法
方案1:调整代码顺序(改动最小)
- 把
index.js里的client导出语句,移到加载路由文件之前,确保路由加载时能拿到正确的导出值:
// index.js const client = new Client(/* 你的客户端配置,比如intents等 */); // 提前导出client,再加载路由 module.exports = { client }; const routes = require('./routes.js'); https.createServer(options, app).listen(PORT, function() { console.log('Express server listening on port ' + PORT); }); app.use(bodyParser.json(), routes, limiter); client.login(token).then(() => {console.info('Logged in.');});
- 修改
routes.js的导入方式,不要在加载时直接解构client,避免CommonJS值拷贝导致拿不到最终实例:
// routes.js const express = require('express'); const router = express.Router(); const { invite, topggAuth } = require('./config.json'); // 导入整个index模块对象,不要提前解构client const appModule = require('./index.js'); const vote = require('./events/vote.js'); router.route('/api/tggwh').post((req, res) => { if (req.header('authorization') === topggAuth) { // 请求处理时再取client属性,确保拿到有效实例 vote.execute(appModule.client, req.body); res.status(200).end(); } else { console.log('Unauthorized vote request attempt.'); res.status(401).send('Unauthorized').end(); } }); module.exports = router;
- 优化
vote.js的用户获取逻辑,不要强依赖本地缓存:
// vote.js const { logUsage } = require('../functions.js'); module.exports = { name: 'vote', async execute(client, data) { try { // 用fetch方法拉取用户,自动处理缓存未命中的情况 const targetUser = await client.users.fetch(data.user); await targetUser.send('Thank you for voting!'); logUsage({ name: 'vote' }); } catch (error) { console.error('Whoops! Something went wrong while thanking a vote.'); console.error(error); } }, };
方案2:抽离client实例(彻底规避循环依赖)
如果不想调整现有代码顺序,可以单独新建client.js文件专门维护客户端实例,从根源消除循环引用:
// client.js const { Client } = require('discord.js'); // 单独初始化并导出client const client = new Client(/* 你的客户端配置 */); module.exports = client;
之后index.js、routes.js、vote.js全部从这个独立文件导入client即可,不会再出现加载顺序导致的取值错误。
优化建议:可以等client触发
ready事件后再启动HTTP服务,避免服务刚启动、客户端还没完成Discord连接时收到请求,导致接口调用失败。
内容的提问来源于stack exchange,提问作者johng3587
相关产品推荐
相关产品推荐

