【问题标题】:Can't resolve child_process when using Nodemailer in Next.js在 Next.js 中使用 Nodemailer 时无法解析 child_process
【发布时间】:2021-02-06 05:20:19
【问题描述】:

在其中一个页面/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.js pages/api 路由仅在服务器环境中运行,因此发生此错误是一个奇迹。如何解决这个问题,以便我可以向我的用户发送电子邮件更新?

codesandbox.io 处添加了一个示例。我认为我们需要直接在本地机器上构建程序的副本才能重现。

Nextjs issue discussion

【问题讨论】:

    标签: react-redux next.js child-process nodemailer


    【解决方案1】:

    我已经找到了来源。使用 Next.js 时,包括在构建期间解析本地服务器资源的 NPM 包在内的所有模块都需要导入到仅限服务器端的模块中。这不像在通用网络应用程序中听起来那么简单。

    在通用模块中执行类似以下人为的示例将导致如下错误: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 分支,并在仅服务器端模块中手动导入服务器电子邮件逻辑,在仅浏览器模块中导入浏览器电子邮件逻辑。在大型应用程序中,如果不是不可能处理,这可能会变得非常麻烦。不建议手动执行此操作。

    【讨论】:

      【解决方案2】:

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

      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
      

      因此触发了错误。解决方案,上面已经提到的一些是:

      1. 使用更具体的导入import { clientSideMethod } from '../../node_modules/mypackage/dist/utils'
      2. 在需要时动态导入服务器端模块
      3. 将包或模块拆分为客户端和服务器端

      解决起来很简单,但很难找到原因!

      【讨论】:

        猜你喜欢
        • 2017-12-22
        • 2022-11-09
        • 2021-06-23
        • 2021-12-25
        • 2017-04-24
        • 1970-01-01
        • 2021-01-12
        • 2018-11-18
        • 2019-04-24
        相关资源
        最近更新 更多