JavaScript >> Javascript 文檔 >  >> JavaScript

如何使用 Nodemailer 發送電子郵件

了解如何使用 Nodemailer 從您的應用程序配置 SMTP 服務器和發送電子郵件。還學習如何使用 EJS 創建動態 HTML 模板以發送電子郵件。

首先,我們需要通過 NPM 安裝 nodemailer 包:

npm install nodemailer

這會將 Nodemailer 添加到您的應用程序中。如果您使用的是最新版本的 NPM,則還應添加 02 作為應用程序 18 中的依賴項 文件。

選擇 SMTP 提供程序

在我們繼續之前,我們需要確保我們可以訪問 SMTP 提供程序。 SMTP 提供程序是一種服務,它提供對我們實際發送電子郵件所需的 SMTP 服務器的訪問。當你可以 自己創建一個 SMTP 服務器,由於法規遵從性和技術開銷,這通常比它的價值更麻煩。

SMTP 代表簡單郵件傳輸協議。它是一種互聯網標准通信協議,描述了用於通過互聯網發送電子郵件的協議。

在您的應用程序中使用 SMTP 時,標準是使用第三方 SMTP 服務來為您處理合規性和技術部分,這樣您就可以專注於您的應用程序。有很多不同的 SMTP 提供商,每個都有自己的優點、缺點和成本。

我們的建議?郵戳。這是一項付費服務,但是,它具有出色的用戶界面和出色的文檔,可為您節省大量時間和麻煩。如果您想避免付費,另一種類似的服務是 Mailgun。

在繼續之前,請使用 Postmark 設置一個帳戶,然後按照此快速教程訪問您的 SMTP 憑據(接下來我們將需要這些憑據)。

或者,使用 Mailgun 設置一個帳戶,然後按照本教程訪問您的 SMTP 憑據。

準備好 SMTP 提供商和憑據後,讓我們繼續前進。

配置您的 SMTP 服務器

在我們開始發送電子郵件之前,第一步是配置 SMTP 傳輸。一個交通工具 是 Nodemailer 用來描述它將用於實際發送的方法的術語 您的電子郵件。

import nodemailer from 'nodemailer';

const smtp = nodemailer.createTransport({
  host: '',
  port: 587,
  secure: process.env.NODE_ENV !== "development",
  auth: {
    user: '',
    pass: '',
  },
});

首先,我們導入 22 來自 39 我們在上面安裝的包。接下來,我們定義一個變量40 並將其分配給對 55 的調用 .這是重要的部分。

在這裡,我們傳遞了一個選項對象,它告訴 Nodemailer 我們想使用什麼 SMTP 服務來發送我們的電子郵件。

等等,我們不是用我們的應用發送電子郵件嗎?

從技術上講,是的。但是在 Internet 上發送電子郵件需要一個正常工作的 SMTP 服務器。使用 Nodemailer,我們不是創建服務器,而是創建 SMTP 客戶端 .不同之處在於服務器充當實際的發送者 (在技術意義上),而客戶端連接到服務器以將其用作中繼來執行實際發送。

然後,在我們的應用程序中,調用 62 建立與我們的 SMTP 提供程序的客戶端連接。

使用您之前從 SMTP 提供程序獲得的憑據,讓我們更新此選項對象。雖然它們可能不准確,但您的 SMTP 提供商應該使用類似的術語來描述我們需要傳遞的每個設置:

{
  host: 'smtp.postmarkapp.com',
  port: 587,
  secure: process.env.NODE_ENV !== "development",
  auth: {
    user: 'postmark-api-key-123',
    pass: 'postmark-api-key-123',
  },
}

在這裡,我們要替換 72 , 81 , 和 94104 在嵌套的 118 下 對象。

122 應該看起來像 137 . 148 應設置為 587(使用 SMTP 發送電子郵件的安全端口)。

仔細檢查並確保您的設置正確,然後我們就可以繼續發送了。

發送電子郵件

使用 Nodemailer 發送電子郵件很簡單:我們需要做的就是調用 157160 返回的值的方法 我們存儲在 170 上面的變量,像這樣:

smtp.sendMail({ ... })

接下來,我們需要傳遞適當的消息配置來發送我們的電子郵件。消息配置對像傳遞給 183 並包含像 197 這樣的設置 , 208 , 219 , 和 221 .

舉個簡單的例子,讓我們傳遞啟動電子郵件所需的最低限度的設置:

[...]

smtp.sendMail({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Testing Email Sends',
  html: '<p>Sending some HTML to test.</p>',
});

很清楚。這裡我們傳入一個 230 , 246 , 259 , 和 263 設置來指定我們的電子郵件將發送給誰、來自哪裡、幫助收件人識別電子郵件的主題以及在電子郵件正文中發送的一些 HTML。

而已!嗯,這就是基本 版本。如果你看一下 Nodemailer 的消息配置文檔,你會發現有幾個選項可以通過。

為了確保這一切都清楚,讓我們看一下到目前為止的完整示例代碼:

import nodemailer from 'nodemailer';

const smtp = nodemailer.createTransport({
  host: 'smtp.someprovider.com',
  port: 587,
  secure: process.env.NODE_ENV !== "development",
  auth: {
    user: 'smtp-username',
    pass: 'smtp-password',
  },
});

smtp.sendMail({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Testing Email Sends',
  html: '<p>Sending some HTML to test.</p>',
});

現在,雖然這在技術上是可行的,但如果我們將其逐字複製並粘貼到一個普通文件中,當我們運行代碼時,我們會立即發送我們的電子郵件。這可能是個大問題。

讓我們稍微修改一下這段代碼:

import nodemailer from 'nodemailer';

const smtp = nodemailer.createTransport({
  host: 'smtp.someprovider.com',
  port: 587,
  secure: process.env.NODE_ENV !== "development",
  auth: {
    user: 'smtp-username',
    pass: 'smtp-password',
  },
});

export default (options = {}) => {
  return smtp.sendMail(options);
}

等待!我們的示例選項去了哪裡?

我們不太可能希望在我們的應用程序啟動後立即發送電子郵件。為了使我們可以手動發送電子郵件,我們將調用包裝到 276 與另一個採用 280 的函數 對像作為參數。

你能猜出那個選項對象包含什麼嗎?沒錯,我們缺少的選項。

這段代碼和上面的不同之處在於我們可以在應用程序的其他地方導入這個文件,在我們要發送電子郵件的地方調用導出的函數。

例如,假設上面的代碼位於路徑 299 在我們的應用程序中:

import sendEmail from '/lib/email/send.js';
import generateId from '/lib/generateId.js';

export default {
  createCustomer: (parent, args, context) => {
    const customerId = generateId();
    await Customers.insertOne({ _id: customerId, ...args.customer });
    
    await sendEmail({
      to: '[email protected]',
      from: '[email protected]',
      subject: 'You have a new customer!',
      text: 'Hooray! A new customer has signed up for the app.',
    });

    return true;
  },
};

這應該看起來很熟悉。同樣,我們在這裡使用來自 Nodemailer 的完全相同的消息配置對象。唯一的區別是,現在 Nodemailer 不會發送我們的電子郵件,直到我們調用 307 功能。

驚人的。所以,既然我們知道如何實際發送電子郵件,讓我們更進一步,讓它在我們的應用程序中更有用。

使用 EJS 創建動態模板

如果您是 Pro 訂閱者並有權訪問本教程的存儲庫,您會注意到此功能內置於該存儲庫所基於的樣板,即 CheatCode Node.js 樣板。

該代碼與我們目前看到的示例之間的區別在於它包含一個特殊功能:定義自定義 HTML 模板並使用調用 313 .

讓我們看一下整個設置並逐步完成。

/lib/email/send.js

import nodemailer from "nodemailer";
import fs from "fs";
import ejs from "ejs";
import { htmlToText } from "html-to-text";
import juice from "juice";
import settings from "../settings";

const smtp = nodemailer.createTransport({
  host: settings?.smtp?.host,
  port: settings?.smtp?.port,
  secure: process.env.NODE_ENV !== "development",
  auth: {
    user: settings?.smtp?.username,
    pass: settings?.smtp?.password,
  },
});

export default ({ template: templateName, templateVars, ...restOfOptions }) => {
  const templatePath = `lib/email/templates/${templateName}.html`;
  const options = {
    ...restOfOptions,
  };

  if (templateName && fs.existsSync(templatePath)) {
    const template = fs.readFileSync(templatePath, "utf-8");
    const html = ejs.render(template, templateVars);
    const text = htmlToText(html);
    const htmlWithStylesInlined = juice(html);

    options.html = htmlWithStylesInlined;
    options.text = text;
  }

  return smtp.sendMail(options);
};

這裡有很多額外的東西,所以讓我們先關注熟悉的東西。

從調用 325 開始 ,請注意,我們調用的代碼與上面完全相同。唯一的區別是,這裡不是直接傳遞我們的設置,而是依賴於 CheatCode Node.js 樣板中的內置設置約定。

接下來,我們要查看文件的最底部。對 335 的調用 應該看起來很熟悉。事實上,這與我們在上面將調用包裝在接受選項對象的函數中時看到的模式完全相同。

添加模板功能

現在是棘手的部分。您會注意到我們在文件頂部添加了很多導入。除了 345 ,我們添加了:

  • 356 - 無需安裝。這是 Node.js 核心內置的文件系統包。它使我們能夠訪問文件系統,例如讀取和寫入文件。
  • 363 - 我們將用於替換 HTML 電子郵件模板中的動態內容的庫。
  • 371 - 一個庫,我們將使用該庫自動將編譯後的 HTML 轉換為文本,以提高用戶對電子郵件的可訪問性。
  • 383 - 用於自動內聯任何 399 的庫 HTML 電子郵件模板中的標籤。

如果您沒有使用 CheatCode Node.js Boilerplate,請繼續安裝最後三個依賴項:

npm install ejs html-to-text juice

現在,讓我們仔細看看這個示例底部導出的函數。這個函數在技術上與我們之前看到的包裝函數相同,但有一個很大的不同:我們現在預計可能會出現 401411 除了我們目前看到的消息配置之外,還傳遞了值。

而不是僅僅接受 424 然而,我們盲目地使用對象解構來從選項對像中“摘下”我們想要的屬性——有點像葡萄。一旦我們有了 435445 屬性(葡萄),我們將其餘選項收集在一個名為 454 的新變量中 使用 468 JavaScript 擴展運算符。

接下來,在函數頂部的函數體內,我們定義了一個變量 471 指向我們的 HTML 電子郵件模板的計劃位置:480 .

在這裡,我們傳遞 497 我們從 503 解構的屬性 對像傳遞給我們的新函數(同樣,已經包含在 CheatCode Node.js 樣板中的那個)。 注意事項 :即使我們使用的是名稱 514 在這裡,該值被分配給我們作為 528 傳遞的選項對象 .

為什麼改名?好吧,如果我們再往下看,我們要確保變量名 533 我們仍然可以訪問。因此,我們通過編寫 544 來利用 JavaScript 中重命名解構屬性的能力 .這裡,557569 之後 告訴 JavaScript 我們要在當前函數的範圍內將該變量中的值分配給一個新名稱。

明確一點:我們不是 在此處永久更改或改變選項對象。我們只是在這個函數的主體中臨時更改名稱——給它一個別名;沒有別的地方。

接下來,一旦我們有了模板路徑,就可以開始工作了。

首先,我們設置一個新的573 包含我們 585 的“解包”版本的對象 使用 JavaScript 擴展運算符的變量。我們在這裡這樣做是因為在這一點上,我們只能確定傳遞給我們函數的選項對象包含 Nodemailer 消息配置選項。

為了確定我們是否使用模板發送電子郵件,我們編寫了 593 聲明說“如果有一個 600 現在和 616 620 返回 true 我們在上面寫過,假設我們有一個要編譯的模板。”

如果 630645 檢查失敗,我們將跳過任何模板編譯並移交我們的 651 直接指向 665 .

但是,如果我們這樣做 有一個模板,它確實 存在於路徑中,接下來,我們使用 672 獲取 HTML 模板的原始內容並將它們存儲在 683 多變的。接下來,我們使用 698 方法,傳遞我們要替換內容的 HTML 模板,然後是 703 包含該文件替換的對象。

因為我們正在編寫代碼來支持任何 模板(不是特定的),讓我們快速看一下示例 HTML 模板,以確保這不會造成混淆:

/lib/email/templates/reset-password.html

<html>
  <head>
    <title>Reset Password</title>
  </head>
  <style>
    body {
      color: #000;
      font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
      font-size: 16px;
      line-height: 24px;
    }
  </style>
  <body>
    <p>Hello,</p>
    <p>A password reset was requested for this email address (<%= emailAddress %>). If you requested this reset, click the link below to reset your password:</p>
    <p><a href="<%= resetLink %>">Reset Your Password</a></p>
  </body>
</html>

在這裡,我們有一個帶有 715 的純 HTML 文件 標籤包含一些通用顏色和字體樣式以及一個簡短的 724 包含我們電子郵件的內容。

請注意,在裡面我們有一些奇怪的、非標準的 HTML 標籤,比如 737 .這些被稱為 EJS 標籤,被設計為佔位符,EJS 將從我們的 747 中“吐出”相應的值 對像到模板中。

換句話說,如果我們的 751 對像看起來像這樣:

{
  emailAddress: '[email protected]',
  resetLink: 'https://justatest.com',
}

我們希望從 EJS 中得到這樣的 HTML:

<body>
  <p>Hello,</p>
  <p>A password reset was requested for this email address ([email protected]). If you requested this reset, click the link below to reset your password:</p>
  <p><a href="https://justatest.com">Reset Your Password</a></p>
</body>

現在,回到我們的 JavaScript 代碼,在我們取回 765 之後 770 中的字符串 ,我們將它傳遞給 789 我們導入的方法來取回一個無 HTML 的純文本字符串(同樣,這用於可訪問性——電子郵件客戶端回退到 798 如果 HTML 版本有問題,電子郵件的版本)。

最後,我們取800 再次將其傳遞給 813 內聯 826 我們在頂部看到的標籤。內聯是添加 830 中包含的樣式的過程 通過 848 直接標記 HTML 元素 屬性。這樣做是為了確保樣式與所有電子郵件客戶端兼容,不幸的是,這些客戶端遍布地圖。

一旦我們有了編譯好的 850 和我們的 864 ,作為我們的最後一步,在 873 的底部 語句,我們分配 889899 到我們的 906 和我們的 910 值。

完畢!現在,當我們調用我們的函數時,我們可以傳入一個 921 name(對應932中HTML文件的名稱 目錄)以及一些 940 向我們的用戶發送動態呈現的 HTML 電子郵件。

讓我們看看使用這個函數來總結一下:

await sendEmail({
  to: args.emailAddress,
  from: settings?.support?.email,
  subject: "Reset Your Password",
  template: "reset-password",
  templateVars: {
    emailAddress: args.emailAddress,
    resetLink,
  },
});

與我們之前看到的幾乎相同,但請注意:這次我們傳遞了一個 956 名稱和 968 向我們的函數發出信號,我們要使用 978 模板並將其 EJS 標記替換為 988 中的值 對象。

說得通?如果沒有,請隨時在下方發表評論,我們會為您提供幫助!


Tutorial JavaScript 教程
  1. getLastRow() 在第 68 行停止

  2. 什麼是 AngularJS 指令?

  3. Shopify 推薦產品 AJAX API 和部分

  4. 我使用並始終傳遞的學習指南(javascript)

  5. 使用 WebRTC 和 Twilio 創建實時視頻聊天室

  6. 初學者 React 教程中的 Firebase [2019]

  7. Search Dragon - 我的搜索引擎網絡應用🔍🐲

  1. #NeedToNode 回顧:介紹 Node.js v6 LTS Boron

  2. 沒有 jQuery 的 JavaScript 向下滑動

  3. 承諾

  4. Supabase - 快速入門:Vue.js

  5. 給開發者更多的意志力

  6. 你絕對可以使用全局變量來管理 React 中的全局狀態

  7. 使用 React、styled-components 和 react hooks 創建一個可搜索的電影應用程序

  1. React Native 中的導航:從零開始。

  2. 使用 esbuild 觀察和構建代碼

  3. 每個 React 開發人員都應該遵循的 10 個最佳實踐

  4. 自定義光標