React redux 在Next.js中使用nodeEmailer时无法解析子进程

React redux 在Next.js中使用nodeEmailer时无法解析子进程,react-redux,next.js,child-process,nodemailer,React Redux,Next.js,Child Process,Nodemailer,在其中一个页面/api路由中使用NodeEmailr会产生以下错误消息: [错误]。/node_modules/nodemailer/lib/sendmail transport/index.js未找到模块:无法解析'C:\ua demo\node_modules\nodemailer\lib\sendmail transport'中的'child_进程' 据我所知,next.js页面/api路由仅在服务器环境中运行,因此发生此错误是一个奇迹。如何解决这个问题,以便我可以向我的用户发送电子邮件更

在其中一个页面/api路由中使用NodeEmailr会产生以下错误消息:

[错误]。/node_modules/nodemailer/lib/sendmail transport/index.js未找到模块:无法解析'C:\ua demo\node_modules\nodemailer\lib\sendmail transport'中的'child_进程'

据我所知,next.js页面/api路由仅在服务器环境中运行,因此发生此错误是一个奇迹。如何解决这个问题,以便我可以向我的用户发送电子邮件更新

此处添加了一个示例。我认为我们需要直接在本地机器上建立一个程序副本来复制


我已经找到了源头。使用Next.js时,所有模块(包括在构建期间解析本机服务器资源的NPM包)都需要导入到仅服务器端模块中。这并不像在通用web应用程序中听起来那么直截了当

在通用模块中执行以下人为示例将导致如下错误:无法解析“C:\ua demo\node\u modules\NodeEmailr\lib\sendmail transport”中的“child\u process”,因为child\u 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;
将此“发送邮件”模块导入组件时,请相信浏览器检查可确保在运行时使用适当的发送电子邮件逻辑。但是,构建失败,并出现与上面类似的错误。这里的解决方案是修改sendmail模块,将其导入延迟到运行时

// 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环境变量,并使用在生成时分支代码,而不是动态运行时加载,如中所示:

// #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分支,并在仅服务器端模块中手动导入服务器电子邮件逻辑,在仅浏览器模块中手动导入浏览器电子邮件逻辑。在大型应用程序中,如果不是不可能处理的话,这可能会变得相当麻烦。手动执行此操作是不可取的。

在我的案例中,我导入了一个简单的方法,该方法似乎只有客户端代码:

import { clientSideMethod } from 'mypackage'
clientSideMethod存在于:

./node_modules/mypackage/dist/utils.js
但错误来自一个完全不同的文件:

Module not found: Can't resolve 'child_process'
error - ./node_modules/mypackage/dist/file.js:8:0
似乎使用通用包导入时,不管是否实际调用了这些方法,都会遍历下面的所有文件:

import { clientSideMethod } from 'mypackage'
实际上,首先转到包的根目录并调用:

export * from './file'; // contains server-side code
export * from './utils'; // contains client-side code only
因此触发了错误。以上已经提到的解决方案包括:

从“../../node_modules/mypackage/dist/utils”使用更具体的导入{clientSideMethod}” 需要时动态导入服务器端模块 将包或模块分为客户端和服务器端 修复起来非常简单,但找到原因却令人沮丧