react-redux - 在 Next.js 中使用 Nodemailer 时无法解析 child_process
问题描述
在其中一个页面/api 路由中使用 nodemailer 会产生以下错误消息:
[ error ] ./node_modules/nodemailer/lib/sendmail-transport/index.js Module not found: Can't resolve 'child_process' in 'C:\ua-demo\node_modules\nodemailer\lib\sendmail-transport'
据我了解,next.jspages/api
路由仅在服务器环境中运行,因此发生此错误是一个奇迹。如何解决这个问题,以便我可以向我的用户发送电子邮件更新?
在codesandbox.io添加了一个示例。我认为我们需要直接在本地机器上构建程序的副本才能重现。
解决方案
我发现了源头。使用 Next.js 时,包括在构建期间解析本地服务器资源的 NPM 包在内的所有模块都需要在仅服务器端模块中导入。这不像在通用 Web 应用程序中听起来那么简单。
在通用模块中执行以下人为设计的示例Can't resolve 'child_process' in 'C:\ua-demo\node_modules\nodemailer\lib\sendmail-transport'
将导致如下错误:因为child_process
是本机服务器资源。
// send-mail/server.js
import nodeMailer from 'nodemailer';
import config from './some/nodemailer/config;
const transport = nodeMailer.createTransport( config );
const sendMail = message => transport.sendMail( message );
export default sendMail;
// send-mail/browser.js
import { post } from 'axios';
const sendMail = async ( axiosRequestConfig ) => {
try {
await post( axiosRequestConfig );
} catch( e ) {
console.error( e );
}
};
export default sendMail;
// send-mail/index.js
import isBrowser from './some/browser/detection/logic';
import browserMailer from './browser';
import serverMailer from './server';
const mailer = isBrowser() ? browserMailer : serverMailer;
export default mailer;
将此“发送邮件”模块导入您的组件,并相信浏览器检查可确保运行时适当的发送电子邮件逻辑。但是,构建失败并出现与上述错误类似的错误。这里的解决方案是修改send-mail
模块以将其导入推迟到运行时。
// send-mail/index.js
import dynamic from 'next/dynamic'; // Can also use other lazy-loading module mechanism here. Since we are building a next.js app here, why not use the one created specifically for next apps?
import isBrowser from './some/browser/detection/logic';
const mailer = isBrowser()
? dynamic(() => import( './server' ))
: dynamic(() => import( './browser' ));
export default mailer;
如果使用 webpack,我们可以RUN_TARGET=BROWSER
为客户端构建设置环境变量,并使用webpack-conditional-loader在构建时分支代码,而不是动态运行时加载,如下所示:
// #if process.env.RUN_TARGET !== 'BROWSER'
import serverMailer from './server';
// #endif
// #if process.env.RUN_TARGET === 'BROWSER'
import browserMailer from './browser';
// #endif
let mailer;
// #if process.env.RUN_TARGET !== 'BROWSER'
mailer = serverMailer;
// #endif
// #if process.env.RUN_TARGET === 'BROWSER'
mailer = browserMailer;
// #endif
export default mailer;
// yeilds the following after server-side build
import serverMailer from './server';
let mailer;
mailer = serverMailer;
export default mailer;
// yeilds the following after client-side build
import browserMailer from './browser';
let mailer;
mailer = browserMailer;
export default mailer;
还可以选择删除 index.js 分支,并在仅服务器端模块中手动导入服务器电子邮件逻辑,在仅浏览器模块中导入浏览器电子邮件逻辑。在大型应用程序中,如果不是不可能处理,这可能会变得非常麻烦。不建议手动执行此操作。
推荐阅读
- vba - VBA - 从 ASP 页面检索数据
- ffmpeg - 使用复杂过滤器 amerge 的 FFmpeg 无法在 iOS 上播放
- javascript - 如何在状态之间为图像设置动画
- ios - Scenekit 错误:C3DNode finalize 中发现的场景图不一致 - 无能为力
- python - Scrapy webscraping输出页面中缺少的元素
- c# - System.Collections.Immutable 容器,为什么要密封?
- sql - VB.NET SQL 查询失败。必须声明 Scalar 但它已经被删除
- android - 如何构建 64 位 hvx 库?
- c++ - 顶点和索引缓冲区如何在 DirectX11 中使用顶点、法线和 Texcoords 工作
- c# - 正则表达式识别有效文件路径和文件名太慢