Qwik应用部署Vercel时Nodemailer导致构建失败求助
问题描述
部署Qwik应用到Vercel时,只要导入nodemailer就触发构建失败,报错信息如下:
[commonjs--resolver] Cannot bundle Node.js built-in "events" imported from "node_modules/nodemailer/lib/mailer/index.js". Consider disabling ssr.noExternal or remove the built-in dependency.
已尝试操作:
- 将nodemailer升级至最新版本(6.9.4)
- 在Vite配置中添加
ssr.noExternal - 移除nodemailer后构建成功,但业务需要保留该库用于邮件发送
依赖版本:Qwik 1.2.6、Nodemailer 6.9.4、Vercel CLI 29.1.1、Node.js v18.17.1
解决方案
1. 仅在服务器端API端点中使用Nodemailer
Nodemailer是Node.js专属库,绝对不能在客户端组件中导入。正确的做法是将邮件发送逻辑封装到Qwik的服务器端API端点中,客户端通过HTTP请求调用该端点触发邮件发送。
示例:创建src/routes/api/send-email.ts端点
import { type RequestHandler } from '@builder.io/qwik-city'; import nodemailer from 'nodemailer'; export const onPost: RequestHandler = async ({ request, response }) => { const { to, subject, text } = await request.json(); // 配置Nodemailer传输器 const transporter = nodemailer.createTransport({ service: 'Gmail', // 替换为你的邮件服务 auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS } }); const mailOptions = { from: process.env.EMAIL_USER, to, subject, text }; try { await transporter.sendMail(mailOptions); response.status = 200; response.json({ success: true }); } catch (error) { response.status = 500; response.json({ success: false, error: (error as Error).message }); } };
客户端通过fetch调用该API:
// 客户端组件中 const sendEmail = async () => { const res = await fetch('/api/send-email', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: 'recipient@example.com', subject: 'Test Email', text: 'Hello from Qwik + Vercel!' }) }); const data = await res.json(); console.log(data); };
2. 正确配置Vite的SSR外部依赖
如果因业务需要在SSR场景下使用Nodemailer,调整vite.config.ts的SSR配置,将Nodemailer及其依赖的Node.js内置模块标记为外部依赖,避免Vite尝试打包它们:
import { defineConfig } from 'vite'; import { qwikVite } from '@builder.io/qwik/optimizer'; import { qwikCity } from '@builder.io/qwik-city/vite'; export default defineConfig(() => { return { plugins: [qwikCity(), qwikVite()], ssr: { // 将Nodemailer和Node.js内置模块设为外部依赖,由运行时Node环境提供 external: ['nodemailer', 'events', 'stream', 'crypto'] } }; });
注意:ssr.external与ssr.noExternal作用相反——前者告知Vite无需打包指定模块,后者强制打包。此处使用external更符合Node.js库的运行逻辑。
3. 确认Vercel构建配置
确保Vercel使用正确的构建命令和输出目录,可在项目根目录创建vercel.json:
{ "buildCommand": "npm run build", "outputDirectory": "dist" }
同时在Vercel控制台的项目设置中,确认运行环境为Node.js 18.x,与本地开发版本保持一致。
4. 用环境变量存储敏感信息
避免在代码中硬编码邮箱账号、密码等敏感数据,通过Vercel控制台的「环境变量」页面添加EMAIL_USER、EMAIL_PASS等变量,确保部署时能正确读取。
内容的提问来源于stack exchange,提问作者Jamal Makame

