开发指南邮件系统
邮件系统
🚀 Cheatsheet (快速执行)
目标: 15分钟内配置邮件发送功能,让用户可以收到注册确认邮件
最快上手方式:
# 1. 配置邮件服务 (使用 Gmail SMTP)
# 前往 https://myaccount.google.com/apppasswords 生成应用密码
# 2. 配置环境变量
echo 'SMTP_HOST=smtp.gmail.com' >> .env.local
echo 'SMTP_PORT=587' >> .env.local
echo 'SMTP_USERNAME=你的邮箱@gmail.com' >> .env.local
echo 'SMTP_PASSWORD=你的应用密码' >> .env.local
echo 'FROM_EMAIL=你的邮箱@gmail.com' >> .env.local
# 3. 测试邮件发送
# 注册新用户,检查邮箱是否收到验证邮件
立即生效: 用户注册后自动收到邮箱验证邮件,支持密码重置邮件
❓ 为什么要配置邮件系统
对 MVP 的核心价值:
- 用户信任: 邮箱验证让用户相信你的产品是正规的
- 账号安全: 密码重置功能必需邮件验证
- 用户激活: 通过邮件引导用户回到产品
- 营销触达: 产品更新、活动通知直达用户邮箱
真实场景举例: 用户注册你的 AI 写作工具,输入邮箱后收到一封精美的欢迎邮件,引导完成设置,用户感觉很专业,提高了对产品的信任度。
🤔 为什么选择 React Email + SMTP
我们的实战经验:
- 开发体验好: 像写 React 组件一样写邮件模板
- 成本可控: SMTP 免费额度高,第三方服务贵
- 样式现代: Tailwind CSS 让邮件美观专业
- 类型安全: TypeScript 支持,减少邮件发送错误
对比其他方案:
SendGrid/Mailgun: 功能强大但收费,小项目用不上
纯 HTML 邮件: 维护困难,样式兼容性差
Nodemailer: 配置复杂,模板管理不方便
React Email: ✅ 现代化、易维护、免费
🧠 简要原理 & 最简案例
邮件发送流程: 触发事件 → 选择邮件模板 → 填充数据 → 通过 SMTP 发送 → 用户收到邮件
最简发送示例:
// 1. 邮件模板 (src/lib/mail/emails/Welcome.tsx)
import { Button, Html, Text } from '@react-email/components'
interface WelcomeEmailProps {
username: string
verifyUrl: string
}
export function WelcomeEmail({ username, verifyUrl }: WelcomeEmailProps) {
return (
<Html>
<Text>Hi {username}!</Text>
<Text>Welcome to our platform. Please verify your email:</Text>
<Button href={verifyUrl}>Verify Email</Button>
</Html>
)
}
// 2. 发送邮件函数 (src/lib/mail/send.ts)
import { render } from '@react-email/render'
import { createTransport } from 'nodemailer'
export async function sendWelcomeEmail(email: string, username: string) {
const verifyUrl = `${process.env.NEXT_PUBLIC_SITE_URL}/verify?email=${email}`
const emailHtml = render(WelcomeEmail({ username, verifyUrl }))
const transporter = createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: false,
auth: {
user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD,
},
})
await transporter.sendMail({
from: process.env.FROM_EMAIL,
to: email,
subject: 'Welcome! Please verify your email',
html: emailHtml,
})
}
🛠️ 实际操作步骤
1. 获取邮件服务凭据 (5分钟)
使用 Gmail SMTP (推荐新手):
1. 登录 Gmail 账号
2. 前往 https://myaccount.google.com/security
3. 开启两步验证
4. 前往 https://myaccount.google.com/apppasswords
5. 选择 "邮件" 和 "其他设备"
6. 生成应用密码,复制保存
或使用 Resend (推荐生产环境):
1. 访问 https://resend.com 注册账号
2. 验证域名 (可选,用子域名也行)
3. 创建 API Key
4. 每月免费 3000 封邮件
2. 配置环境变量 (5分钟)
Gmail SMTP 配置:
# .env.local
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your-email@gmail.com
SMTP_PASSWORD=your-app-password
FROM_EMAIL=your-email@gmail.com
FROM_NAME=你的产品名
Resend 配置:
# .env.local
RESEND_API_KEY=re_xxx_your_api_key
FROM_EMAIL=noreply@yourdomain.com
FROM_NAME=你的产品名
3. 创建邮件模板 (10分钟)
欢迎邮件模板:
// src/lib/mail/emails/WelcomeEmail.tsx
import {
Body,
Button,
Container,
Head,
Html,
Img,
Link,
Preview,
Section,
Text,
} from '@react-email/components'
interface WelcomeEmailProps {
username: string
verifyUrl: string
}
export function WelcomeEmail({ username, verifyUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>欢迎加入我们!请验证您的邮箱</Preview>
<Body style={main}>
<Container style={container}>
<Section style={logoContainer}>
<Img
src={`${process.env.NEXT_PUBLIC_SITE_URL}/logo.png`}
width="120"
height="36"
alt="Logo"
/>
</Section>
<Section style={content}>
<Text style={paragraph}>Hi {username},</Text>
<Text style={paragraph}>
欢迎加入我们的平台!为了确保账号安全,请点击下方按钮验证您的邮箱地址。
</Text>
<Button style={button} href={verifyUrl}>
验证邮箱
</Button>
<Text style={paragraph}>
如果按钮无法点击,请复制以下链接到浏览器:
</Text>
<Link href={verifyUrl} style={link}>
{verifyUrl}
</Link>
<Text style={paragraph}>
如果您没有注册过账号,请忽略此邮件。
</Text>
</Section>
</Container>
</Body>
</Html>
)
}
// 样式定义
const main = {
backgroundColor: '#f6f9fc',
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
}
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0 48px',
marginBottom: '64px',
}
const logoContainer = {
textAlign: 'center' as const,
padding: '20px 0',
}
const content = {
padding: '0 48px',
}
const paragraph = {
fontSize: '16px',
lineHeight: '1.4',
color: '#334155',
margin: '16px 0',
}
const button = {
backgroundColor: '#3b82f6',
borderRadius: '5px',
color: '#fff',
fontSize: '16px',
fontWeight: 'bold',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block',
width: '200px',
padding: '14px 7px',
margin: '20px auto',
}
const link = {
color: '#3b82f6',
textDecoration: 'underline',
}
4. 实现邮件发送服务 (10分钟)
邮件发送抽象层:
// src/lib/mail/send.ts
import { render } from '@react-email/render'
import { createTransport } from 'nodemailer'
import { Resend } from 'resend'
// 邮件提供商类型
type EmailProvider = 'smtp' | 'resend'
interface EmailOptions {
to: string
subject: string
html: string
text?: string
}
class MailService {
private provider: EmailProvider
private transporter?: any
private resend?: Resend
constructor() {
// 根据环境变量选择提供商
this.provider = process.env.RESEND_API_KEY ? 'resend' : 'smtp'
this.initialize()
}
private initialize() {
if (this.provider === 'resend') {
this.resend = new Resend(process.env.RESEND_API_KEY!)
} else {
this.transporter = createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: false,
auth: {
user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD,
},
})
}
}
async sendEmail({ to, subject, html, text }: EmailOptions) {
const from = `${process.env.FROM_NAME} <${process.env.FROM_EMAIL}>`
try {
if (this.provider === 'resend') {
await this.resend!.emails.send({
from,
to,
subject,
html,
text,
})
} else {
await this.transporter!.sendMail({
from,
to,
subject,
html,
text,
})
}
console.log(`邮件发送成功: ${to}`)
} catch (error) {
console.error('邮件发送失败:', error)
throw error
}
}
// 渲染 React 邮件组件
renderTemplate(component: React.ReactElement) {
return {
html: render(component),
text: render(component, { plainText: true }),
}
}
}
export const mailService = new MailService()
业务邮件函数:
// src/lib/mail/templates.ts
import { mailService } from './send'
import { WelcomeEmail } from './emails/WelcomeEmail'
import { PasswordResetEmail } from './emails/PasswordResetEmail'
export async function sendWelcomeEmail(email: string, username: string) {
const verifyUrl = `${process.env.NEXT_PUBLIC_SITE_URL}/verify?email=${encodeURIComponent(email)}`
const template = mailService.renderTemplate(
WelcomeEmail({ username, verifyUrl })
)
await mailService.sendEmail({
to: email,
subject: `欢迎加入 ${process.env.FROM_NAME}!`,
...template,
})
}
export async function sendPasswordResetEmail(email: string, resetUrl: string) {
const template = mailService.renderTemplate(
PasswordResetEmail({ email, resetUrl })
)
await mailService.sendEmail({
to: email,
subject: '重置密码',
...template,
})
}
export async function sendNotificationEmail(
email: string,
title: string,
message: string
) {
const template = mailService.renderTemplate(
NotificationEmail({ title, message })
)
await mailService.sendEmail({
to: email,
subject: title,
...template,
})
}
5. 集成到用户注册流程 (5分钟)
在注册API中发送邮件:
// app/api/auth/register/route.ts
import { sendWelcomeEmail } from '@/lib/mail/templates'
export async function POST(request: Request) {
const { email, password, name } = await request.json()
try {
// 创建用户账号
const user = await createUser({ email, password, name })
// 发送欢迎邮件
await sendWelcomeEmail(email, name)
return Response.json({
success: true,
message: '注册成功!请检查邮箱验证邮件'
})
} catch (error) {
console.error('注册失败:', error)
return Response.json({ error: '注册失败' }, { status: 500 })
}
}
💡 常用邮件模板
密码重置邮件
// src/lib/mail/emails/PasswordResetEmail.tsx
export function PasswordResetEmail({ email, resetUrl }: {
email: string
resetUrl: string
}) {
return (
<Html>
<Head />
<Preview>重置您的密码</Preview>
<Body style={main}>
<Container style={container}>
<Text style={paragraph}>
您请求重置 {email} 的密码。
</Text>
<Button style={button} href={resetUrl}>
重置密码
</Button>
<Text style={paragraph}>
此链接将在 24 小时后过期。如果您没有请求重置密码,请忽略此邮件。
</Text>
</Container>
</Body>
</Html>
)
}
系统通知邮件
// src/lib/mail/emails/NotificationEmail.tsx
export function NotificationEmail({ title, message }: {
title: string
message: string
}) {
return (
<Html>
<Head />
<Preview>{title}</Preview>
<Body style={main}>
<Container style={container}>
<Text style={title}>{title}</Text>
<Text style={paragraph}>{message}</Text>
<Button style={button} href={`${process.env.NEXT_PUBLIC_SITE_URL}/dashboard`}>
查看详情
</Button>
</Container>
</Body>
</Html>
)
}
🆘 常见问题解决
问题1: Gmail 发送失败
# 检查清单:
1. 是否开启了两步验证?
2. 是否使用应用密码而不是账号密码?
3. 邮箱是否被 Google 限制?
# 解决方案:
# 使用不同的邮箱服务商或 Resend
问题2: 邮件进入垃圾箱
// 优化发送邮件的内容和格式
1. 添加明确的发件人信息
2. 避免过多的推广用词
3. 确保邮件模板格式正确
4. 配置 SPF/DKIM 记录(生产环境)
问题3: 邮件模板样式问题
# 邮件客户端兼容性限制
1. 使用内联样式而不是 CSS 类
2. 避免使用复杂的 CSS 属性
3. 测试主流邮件客户端显示效果
📊 邮件效果监控
基础统计
// src/lib/mail/analytics.ts
export async function trackEmailSent(email: string, type: string) {
await db.emailLog.create({
data: {
email,
type,
status: 'sent',
sentAt: new Date()
}
})
}
export async function getEmailStats() {
const stats = await db.emailLog.groupBy({
by: ['type', 'status'],
_count: true,
where: {
sentAt: {
gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // 30天内
}
}
})
return stats
}
📎 延伸阅读
技术文档:
- React Email - 现代邮件模板开发
- Nodemailer - Node.js 邮件发送库
邮件服务商:
最佳实践:
下一步: 邮件系统配置完成后,查看 [用户认证]./authentication) 了解如何在认证流程中使用邮件验证。