后端应用程序需要向用户发送电子邮件,以便发送通知并在应用程序界面之外维持沟通。这些电子邮件通常包含针对每个用户的特定信息,例如用户的姓名或地址,因此它们是动态生成的。
本文将指导您使用React Email构建动态邮件模板,将其转换为HTML格式,并利用Go模板将数据插入其中。此外,文中还提供了一个可选章节,向您展示如何使用MailHog发送并测试邮件的送达情况。
要阅读本文,请确保您的计算机上已安装了Go和Node.js。同时,您应对React有一定的基本了解,并对Go模板有所熟悉,不过这些并非严格的要求,因为您可以在实践过程中逐步掌握这些知识。
目录
什么是React Email?
React Email是一个JavaScript库,它可以帮助您使用React构建动态邮件模板。如果您已经掌握了React的基础知识,那么React Email能为您构建动态邮件模板提供更好的开发体验。以下是其中的一些原因:
-
熟悉React的语法:如果您已经了解React,那么React Email就能让您避免学习额外的模板语言、使用效率低下的拖放界面,或从头开始用HTML表格编写邮件模板。
-
可重用的内置组件:React Email提供了许多可直接使用的UI组件,比如按钮和页脚,因此您无需从头开始开发,从而让邮件制作过程更加顺畅且高效。
-
在各种邮件客户端中的一致性:React Email生成的邮件模板已经过测试,在常见的邮件客户端中都能正常显示。这可以消除您对邮件在不同客户端上显示效果不一致的担忧。
-
丰富的邮件开发工具:React Email提供了许多用于预览和评估使用它构建的邮件的功能,例如:
-
本地开发服务器:您可以实时在浏览器中预览移动设备和桌面设备上的邮件显示效果。
-
邮件发送测试功能:您可以将邮件发送到真实的电子邮件地址进行预览。
-
兼容性检测工具:该工具能帮助您了解您的邮件在常见邮件客户端中的支持情况。
-
垃圾邮件评分功能:该功能会分析您的邮件,判断它被标记为垃圾邮件的可能性。
-
-
与Tailwind CSS的集成:Tailwind CSS是一个流行的CSS框架,它提供了许多用于美化HTML页面并使其具备响应式设计的样式。React Email可以轻松地与Tailwind CSS集成,帮助您制作出美观的邮件。
所有这些功能都是免费使用的。
在本文中,您将学习如何从React Email模板生成HTML文件,将其转换为Go模板,并向模板中插入数据以便进行预览。
Go模板
Go语言的html/template包允许您定义可重复使用的HTML模板,这些模板可以使用动态数据来填充内容。这些模板中包含占位符(称为“动作”),Go的模板引擎会在执行过程中对这些占位符进行评估,并将其替换为实际的值。

首先,您需要为该包提供包含Go特定注释的HTML内容。系统会将这些HTML内容转换为Go HTML模板,同时将Go特定的注释转换成模板中的“动作”。随后,使用相应的数据执行这个模板,从而生成包含这些数据的HTML输出结果。
package main
import (
"html/template"
"os"
)
func main() {
tmpl := template.New("hello")
tmpl, _ = tmpl.Parse(`Hello {{.}}
`)
tmpl.Execute(os.Stdout, "Gopher")
}
// 输出结果: Hello Gopher
// 测试链接: https://goplay.tools/snippet/KxbkWPIArz5
在上面的代码示例中:
-
template.New用于创建一个名为“hello”的空模板对象。 -
tmpl.Parse(`Hello {{.}}
`)将HTML字符串`
Hello {{.}}
`解析为Go HTML模板,并将其保存在`tmp`变量中。其中,`{{.}}这部分内容就是作为数据占位符的“动作”;`{{`和`}}被称为分隔符,而`.`则是用于访问数据的标识符。
-
tmpl.Execute(os.Stdout, "Gopher")使用字符串“Gopher”来填充这个占位符,并将生成的HTML输出结果写入控制台。
Go模板的分隔符
在上面的代码示例中,我们使用了双大括号`{{`和`}}作为Go模板中的分隔符。分隔符是Go语言用来标识输入字符串中哪些部分代表“动作”的符号——也就是说,这些部分是需要被解析执行的代码。
您也可以通过调用模板的`Delims`方法来更改分隔符。下面的示例说明了这一点:
package main
import (
"html/template"
"os"
)
func main() {
tmpl := template.New("hello")
tmpl, _ = tmpl.Delims("((", ")").Parse(`Hello ((.))
`)
tmpl.Execute(os.Stdout, "Gopher")
}
// 输出结果: Hello Gopher
// 测试链接: https://goplay.tools/snippet/00RuDzvZYwN
在上面的代码片段中,(( 和 )) 被用作 hello 模板的分隔符。
这一点非常重要,因为你需要设置合适的分隔符,以避免 Go 的默认分隔符与 React 中的花括号在 React Email 模板中发生冲突。
使用 React Email 在 Go 语言中创建动态邮件
下图总结了本文中将要构建的示例应用程序的工作原理:

你将创建一个包含 Go 模板注释的 React Email 模板,然后使用 Node.js 从这个模板生成 HTML 文件。Go 会解析这些 HTML 文件,重新生成 Go 模板并执行它,最后将这些生成的邮件发送出去。
可选地,你可以使用 go-mail 来发送邮件,并利用 MailHog 这个本地 SMTP 服务器在浏览器中预览邮件内容。
设置项目环境
首先,请确保你的电脑上已经安装了 Go 和 Node.js。克隆这个 freeCodeCamp-go-react-email 仓库,然后使用 git checkout 01-setup 命令切换到 01-setup 分支。
该项目在 cmd 目录下包含一个 main.go 文件以及一个 go.mod 文件,同时还包含一个 .gitignore 文件,用于告诉 Git 忽略所有的 node_modules 目录。
在项目的终端中运行 go run cmd/main.go。如果控制台显示 “It works!”,说明你的环境设置正确了,可以继续学习下一节内容。
配置 React Email
在项目根目录下创建一个名为 mailer 的目录,这个目录将用于存放与发送邮件相关的功能代码。
在 mailer 目录中,你会创建一个名为 emails 的 Node.js 项目,这个项目将负责实现 React Email 的相关功能。具体操作步骤如下:
-
在
mailer目录下创建一个名为_emails的目录。该目录的名称以下划线开头,这样在运行go build命令时,这个目录会被忽略,因此不会被包含到编译后的可执行文件中。 -
在
_emails目录的终端中运行npm init -y,以初始化这个 Node.js 项目。这样会在该目录下生成一个package.json文件。 -
将
package.json文件中的 “name” 字段值更改为 “emails”,使包名更加规范。这一步不是必须的。
接下来,请在 `_emails` 目录的根目录下运行以下命令,以安装所需的 React Email 库:
npm install @react-email/ui @types/react -D -E
npm install react-email react react-dom -E
安装完成后,请将package.json文件中的scripts字段替换为以下代码:
"scripts": {
"dev": "email dev --dir ./src",
"export": "email export --pretty --dir ./src --outDir ../templates"
},
dev脚本会启动服务器,以便在浏览器中预览React Email模板。您需要使用React编写这些模板,并将它们保存在src目录下的_emails文件夹中。export脚本会将src目录中的模板文件从JSX(或TSX)格式转换为HTML格式,然后将这些生成的HTML文件保存在名为templates的文件夹中。这个文件夹位于mailer目录下,而不是_emails目录下。
之所以将templates文件夹放在mailer目录下,是因为Go项目只需要templates文件夹中的HTML文件,而不需要_emails文件夹的内容。
如果您已经完成了所有这些步骤,那么就已经在emails这个Node.js项目中配置好了React Email功能。此时,如果您想查看项目的当前状态,请访问freeCodeCamp-go-react-email/02-setup-react-email。
在下一节中,您将创建一个React Email模板,并在浏览器中预览它。
创建一个React Email模板
在这一节中,您将创建一个React Email模板,并在浏览器中预览它。同时,您还会将该模板编译为HTML文件并导出出来。
在_emails目录下创建一个名为src的文件夹。在src文件夹内,创建一个名为welcome.tsx的文件。将以下代码内容复制并粘贴到welcome.tsx文件中。
import {
Body,
Button,
Container,
Head,
Heading,
Html,
Img,
Preview,
Section,
Tailwind,
Text,
} from "react-email";
interface WelcomeEmailProps {
username?: string;
company?: string;
gophers?: string[];
}
const WelcomeEmail = ({
username = "Nicole",
company = "GoWorld",
gophers = ["Tinky Winky", "Dipsy", "Laa-Laa", "Po"],
}: WelcomeEmailProps) => {
const previewText = `欢迎来到\({company}, \){username}!`;
return (
{previewText}</Preview>
{company},{username}!
你好,{username},
我们非常高兴你加入{company}。希望你在我们的团队中度过一段愉快的时光。如果你有任何疑问或需要帮助,请随时联系以下成员:
{gopher}</li>
))}
>
开始使用
祝好!
{company}团队
>
>
上述代码片段就是本文中将要使用的React Email模板。要预览该模板,请进入 `_emails` 根目录的终端窗口,然后运行 `npm run dev` 命令。接着使用浏览器访问终端中显示的预览地址。点击左侧侧边栏中的“welcome”链接,你应该会看到如下截图所示的用户界面:

在上述用户界面中,React Email会使用传递给 `welcome` 模板的默认值来生成邮件内容。
要停止服务器运行,请在终端中按下 `CTRL + C` 来终止相关进程。接下来,在 `_emails` 根目录的终端中运行 `npm run export` 命令,即可将 `src` 目录中的HTML文件构建并导出。这些导出的HTML文件会被保存在 `mailer` 目录下的 `templates` 文件夹中。在 `templates` 文件夹中,你会看到一个名为 `welcome.html` 的文件——它其实就是从 `welcome.tsx` 文件生成的HTML内容。
如果要查看项目的当前状态,请访问以下链接:freeCodeCamp-go-react-email/03-create-react-email-template。
从HTML文件生成Go模板
你已经创建了一个React Email模板,预览了它,构建了它,并将其导出了为HTML文件。在这一部分中,你会修改这个React Email模板,使其使用你自己设定的分隔符来代替React默认的大括号;同时,你还会从该HTML文件生成一个Go模板。
首先,请将 `welcome.tsx` 文件中的内容替换为以下代码片段,这样就可以使用 `((` 和 `))` 作为分隔符,并且也会移除TypeScript类型声明:
import {
Body,
Button,
Container,
Head,
Heading,
Html,
Img,
Preview,
Section,
Tailwind,
Text,
} from "react-email";
const WelcomeEmail = () => {
const previewText = `欢迎来到 ((.Company)), ((.Username))!`;
return (
{previewText}</Preview>
((.Company)), ((.Username))!
你好,((.Username)),
我们非常高兴你加入 ((.Company))!希望你在我们的团队中度过一段愉快的时光。如果你有任何疑问或需要帮助,请随时联系以下工作人员:
((.))</li>
((end))
开始使用
祝好,
<((.Company))>团队
);
};
export default WelcomeEmail;
在 `_emails` 目录的根目录中运行 `npm run export` 命令,即可构建并将这个版本的 React Email 模板导出为 HTML 文件。生成的 HTML 文件会包含 Go 模板注释,当这些注释被 Go 解析时,它们会转化为相应的操作,从而形成可供 Go 应用程序使用的 HTML 模板。
在 `mailer` 目录中创建一个名为 `fs.go` 的文件。该文件中的代码用于将 `templates` 目录中的文件嵌入到 Go 应用程序中以便使用。请将以下代码片段复制并粘贴到 `fs.go` 文件中:
```go
package mailer
import (
"embed"
"io/fs"
)
//go:embed templates/*
var embedded embed.FS
var templateFS, _ = fs.Sub.embedded, "templates")
//go:embed templates/* 这行代码告诉 Go 编译器将当前目录(`mailer` 目录)中的文件嵌入到 Go 应用程序的编译后的二进制文件中。这样,Go 应用程序才能访问这些 HTML 模板文件。`templateFS` 变量用于访问 `templates` 子目录中的文件。
在 `mailer` 目录中再创建一个文件,并将其命名为 `mailer.go`。`mailer.go` 文件中包含用于解析 HTML 文件以生成 Go HTML 模板以及发送电子邮件的代码。请将以下代码片段复制并粘贴到 `mailer.go` 文件中:
package mailer
import (
"html/template"
"io"
)
const (
welcomeMailKey = "welcome_mail"
)
func setUpTemplates() (map[string]*template.Template, error) {
templates := make(map[string]*template.Template)
tmpl := template.New("welcome.html").Delims("((", "))
welcomeEmailTmpl, err := tmpl.ParseFS(templateFS, "welcome.html")
if err != nil {
return nil, err
}
templates[welcomeMailKey] = welcomeEmailTmpl
return templates, nil
}
type Mailer struct {
templates map[string]*template.Template
}
// NewMailer 创建一个新的 Mailer 对象
func NewMailer() (*Mailer, error) {
tpls, err := setUpTemplates()
if err != nil {
return nil, err
}
return &Mailer{
templates: tpls,
}, nil
}
type WelcomEmailData struct {
Username string
Company string
Gophers []string
}
func (mailer *Mailer) WriteWelcomeMail(w io.Writer, data WelcomEmailData) error {
tmpl := mailertemplates[welcomeMailKey]
err := tmpl.Execute(w, data)
return err
}
在上述代码片段中:
- `setUpTemplates` 函数创建了一个模板对象 `tmpl`,并设置了其分隔符。然后它解析 `welcome.html` 文件,将其转换为 Go 模板,并将生成的模板存储在 `welcomeEmailTmpl` 变量中。最后,这个模板被添加到 `templates` 映射中,其中键为 `welcomeMailKey`,函数返回 `templates` 映射。
- `NewMailer` 函数创建并返回一个 `Mailer` 对象,该对象包含了用于操作这些模板的代码。
- `WriteWelcomeMail` 函数用于使用实际的数据来执行欢迎邮件模板。
要查看当前代码库的状态,请访问freeCodeCamp-go-react-email/04-create-golang-template。
在浏览器中渲染动态邮件
在本节中,您将创建一个简单的Web服务器,用于查看其中包含了动态数据的邮件模板渲染结果。
请将main.go文件的内容替换为以下代码片段:
package main
import (
"fmt"
"net/http"
"os"
pkgMailer "github.com/orimdominic/freeCodeCamp-go-react-email/mailer"
)
func main() {
mailer, err := pkgMailer.NewMailer()
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
http.HandleFunc("/mail", func(w http.ResponseWriter, r *http.Request) {
(username := r.URL.Query().Get("username")
company := r.URL.Query().Get("company")
gophers := []string{"Tinky Winky", "Dipsy", "Laa-Laa", "Po"}
err = mailer.WriteWelcomeMail(w, pkgMailer.WelcomEmailData{
Username: username,
Company: company,
Gophers: gophers,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
port := ":8888"
err = http.ListenAndServe(port, nil)
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
}
上述代码首先使用mailer.go文件中的NewMailer函数创建了一个邮件发送对象。在完成错误处理后,它会在端口8888上启动一个简单的Web服务器,并设置GET /mail路由。
GET /mail路由会接收两个查询参数:username和company,这些参数将会被用作邮件内容中的动态数据。通过WriteWelcomeMail函数执行模板后,生成的结果会以HTML格式显示在浏览器中。您可以使用这个路由来测试mailer包的功能。
在启动服务器之前,您应该先构建并导出React Email模板文件,这样您的HTML文件就能始终使用最新的模板内容。为了方便进行构建、导出和运行服务器,您可以使用Makefile文件。
请进入项目根目录的终端窗口,创建一个名为Makefile的文件,然后将以下代码片段复制到其中:
run: email-build
go run cmd/main.go
email-build: mailer/_emails
npm --prefix mailer/_emails run export
上述Makefile中的run脚本会将React Email模板构建并导出为HTML文件,保存到mailer/templates目录中,然后启动Go应用程序。请注意,Makefile中使用的是制表符而不是空格来进行代码缩进。
在项目根目录的终端窗口中运行make run命令,然后在浏览器中访问http://localhost:8888/mail?username=Nicole&company=GoWorld,您就会看到邮件内容在浏览器界面中被渲染出来的效果。
请更换URL中的username和company值,以便使用不同的参数来测试该邮件功能。
通过这种设置,你可以将模板执行后的结果直接发送到你的邮件客户端中,收件人看到的邮件内容将与在浏览器中显示的效果完全一致。
如果你想查看当前代码库的进展状态,请访问freeCodeCamp-go-react-email/05-render-dynamic-email。
使用go-mail和MailHog发送并测试邮件
在上一节中,你提供了数据来执行模板,但邮件内容是在浏览器中显示的,而不是通过邮件客户端。在这一节中,你会使用go-mail来发送邮件,而MailHog则会拦截并显示这些邮件。
这一节是可选的。如果你没有在当地安装MailHog,那么你需要使用Docker Compose来为这个项目配置它。在继续之前,请确保你的电脑上已经安装了Docker Compose。
在终端中,导航到项目的根目录,然后运行go get github.com/wneessen/go-mail来安装go-mail。在项目的根目录下创建一个compose.yml文件,并将下面的代码片段粘贴到其中:
services:
mailhog:
image: mailhog/mailhog
restart: no
logging:
driver: "none" # 禁止保存日志
ports:
- 1025:1025 # SMTP服务器
- 8025:8025 # Web界面
在终端中,导航到项目的根目录,然后运行docker compose up来拉取并启动MailHog SMTP服务器。MailHog会在1025端口上监听邮件,并在http://localhost:8025提供Web界面,你可以通过这个界面查看被拦截的邮件。根据你的网络连接情况,初始的镜像拉取过程可能需要几分钟时间。
请将mailer.go文件的内容替换为下面的代码片段:
package mailer
import (
"html/template"
"io"
"github.com/wneessen/go-mail"
)
const (
welcomeMailKey = "welcome_mail"
sender = "noreply@localhost.com"
)
func setUpTemplates() (map[string]*template.Template, error) {
templates := make(map[string]*template.Template)
tmpl := template.New("welcome.html").Delims("((", "))
welcomeEmailTmpl, err := tmpl.ParseFS(templateFS, "welcome.html")
if err != nil {
return nil, err
}
templates[welcomeMailKey] = welcomeEmailTmpl
return templates, nil
}
type Mailer struct {
client *mail.Client
templates map[string]*template.Template
}
// NewMailer用于创建一个新的邮件发送器对象
func NewMailer() (*Mailer, error) {
tpls, err := setUpTemplates()
if err != nil {
return nil, err
}
c, err := mail.NewClient(
"localhost",
mail.WithPort(1025),
mail.WithTLSPolicy(mail.NoTLS),
)
if err != nil {
return nil, err
}
return &Mailer{
client: c,
templates: tpls,
}, nil
}
type WelcomEmailData struct {
Username string
Company string
Gophers []string
}
func (mailer *Mailer) WriteWelcomeMail(w io.Writer, data WelcomEmailData) error {
tmpl := mailer.templates[welcomeMailKey]
err := tmpl.Execute(w, data)
return err
}
func (mailer *Mailer) SendWelcomeMail(to string, data WelcomEmailData) error {
m := mail.NewMsg()
m.From(sender)
m.To(to)
m.Subject("欢迎来到 " + data COMPANY)
m.SetBodyHTMLTemplate(mailertemplates[welcomeMailKey], data)
err := mailer.client.DialAndSend(m)
return err
}
mailer.go文件中的新变更包括以下内容:
-
引入了go-mail包
-
创建了一个名为
sender的常量,用于存储发件人的电子邮件地址 -
使用go-mail包构建了一个邮件发送客户端
-
在
mailer结构体上添加了SendWelcomeMail方法,该方法会使用welcomeEmailTmpl模板生成电子邮件,然后将其发送给接收者
在main.go文件中,需要将GET /mail路由对应的处理函数从WriteWelcomeMail改为SendWelcomeMail。你可以使用任何你想使用的电子邮件地址;下面的代码示例中使用的是fcc@go.dev:
err := mailer.SendWelcomeMail("fcc@go.dev", pkgMailer.WelcomEmailData{
Username: username,
Company: company,
Gophers: gophers,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprint(w, "电子邮件已发送")
请确保邮件服务器正在运行,你可以在浏览器中访问http://localhost:8025来检查服务器状态。在项目的根目录下,通过运行make run命令启动服务器。再次访问http://localhost:8888/mail?username=Nicole&company=GoWorld来测试该路由的功能是否正常。接下来,访问http://localhost:8025查看邮件服务器的界面,你应该会看到类似下图所示的用户界面:

点击“Welcome to Helix”即可查看邮件内容。
如果你想了解当前代码库的运行状态,可以访问freeCodeCamp-go-react-email/06-send-email。
结论
通过完成本教程的学习,你已经:
-
学会了如何将React Email模板转换为Go语言编写的电子邮件模板
-
了解了如何使用Makefile来运行自定义脚本
-
能够在浏览器中预览生成的电子邮件,并使用MailHog进行测试
现在你再也不用费心去编写原始的HTML代码来制作邮件,也不需要学习新的模板语言了。借助React Email和Go模板,你可以更加轻松地构建并发送美观的电子邮件。



