背景
在项目中的某些操作需要通过邮件的方式通知到用户,并且视觉对邮件有自己的想法,于是就开始自己动手编写(截止目前共12封邮件)。
下面记录并总结一下自己开发过程演变:
最开始
使用拉易网用拖拽的形式按照UI稿拖拽出一个基本框架,然后导出html代码,再在这份html上按照视觉稿上修改。
优点:
-
登陆之后,可以保存自己的模版,再基于自己的模版进行拖拽能减少一部分重复工作。
-
拖拽方便,而且可以设置的属性也挺多。
缺点:
-
拖拽生成导出的代码很冗余,可读性差,修改起来很麻烦(伤眼睛....., 效果与源码大概如下图)
-
写inline样式很麻烦(在导出后的html里写,更麻烦)
-
无法持续维护:拉易网上只能保存自己最开始拖拽后生成的框架,后续的开发都是基于导出后的html。
结果: 初步可行,硬着头皮做完了,兼容性问题在部分客户端上还是会有,维护成本太高。
项目后续的版本提出了优化邮件的需求,明确了要兼容的邮件客户端。(web-outlook&hotmail、web-gmail),修改了部分原来邮件的样式并且新增了几封邮件。
回想起上述方案的缺点.... 最终决定寻求新道路。
通过各种途径搜索到以下几个资源:
1、www.litmus.com/ (据说业界都是用它的解决方案,可以方便的查看邮件在各个客户端上的表现等等功能....,可惜是收费的,免费注册试用还要提供信用卡 → 放弃)
2、www.campaignmonitor.com/css/ (好像也是一个卖解决方案的,但是它列举了各个邮件客户端支持的css,算一个CSS兼容性指南吧)
3、app.postdrop.io/ (最后找到了一个在线编辑工具,可以写class,最后导出时转化为inline样式,进阶版收费)
基于以上的资源,于是有了我以下的开发过程:
现在
直接在postdrop上基于它提供的基础模版进行开发,如下图。
优点:
-
模版中提供了一些全局的Reset样式,考虑了一些兼容性、手机端体验的场景。(总比我自己想的要多)
-
可以写一些公共的class(如下图),在需要的元素上使用,最后导出时都会自动转换成inline样式。
-
支持模版保存&在线编辑html,比拉易网的只能拖拽更加灵活,新旧邮件的开发与维护都可以在这个网页中搞定。
-
支持直接向已经绑定验证的邮箱发送测试邮件。
-
支持无限次的导出/下载
缺点:
-
免费计划最多支持保存5个模版。
-
免费计划每天最多能发5封测试邮件。
-
免费计划最多能够绑定2个收件人。
总结:与拉易网的方案相比,这种方法无论是可维护性、便捷性还有开发体验都得到了质的飞跃。
好在这些缺点都可以想办法绕过.......
缺点1-模版数量上限:
解决方案:直接把所有邮件合成一封来写,
方案优点:
-
调试更加方便,右边就能看见所有的邮件,发送测试邮件时也不需要一封一封验证兼容性。
-
公共的class也能更好的复用,不需要各个模版里重新写一遍class
方案缺点:
- 最后导出的邮件都在一个html里。(总不能让后台自己再去拆一封封邮件吧.....,于是就写个拆分邮件的工具)
// splitEmail.js
const fs = require("fs");
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
// 导出的邮件html合集
const html = fs.readFileSync("./uc_all.html", {
encoding: "utf-8",
});
const { document } = new JSDOM(html).window;
// 提取通用的部分
const list = document.querySelectorAll("table.body");
document.body.innerHTML = "";
const bootstrap = document.children[0].outerHTML;
const emails = [].slice.call(list);
const outputPath = "./output";
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath);
} else {
// 清空已存在文件夹
const files = fs.readdirSync(outputPath);
files.forEach((f) => {
fs.unlinkSync(`${outputPath}/${f}`);
});
}
emails.forEach((email) => {
const title = email.getElementsByClassName("title")[0].innerHTML;
fs.writeFileSync(
`${outputPath}/${title}.html`,
`${bootstrap.replace("</body></html>", `${email.outerHTML}</body></html>`)}`
);
});
缺点2、3-收件人、测试邮件的数量上限:
解决方案:反正可以无限次导出html,那就自己写一个发送邮件工具。
const nodemailer = require("nodemailer");
const fs = require("fs");
// 发送单封邮件
// const html = fs.readFileSync("./output/New application.html", {
// 发送合集
const html = fs.readFileSync("./uc_all.html", {
encoding: "utf-8",
});
let transporter = nodemailer.createTransport({
host: "smtp.exmail.qq.com",
port: 465,
auth: {
user: "xxx@xxx.com", // 填你自己的
pass: "xxxx", // 填你自己的
},
});
// 收件人
const tos = [
"xxxx@outlook.com",
// "xxxx@hotmail.com",
// "xxxx@gmail.com",
// "xxxx@126.com",
// "xxxx@qq.com",
];
var message = {
from: "xxxxx@xxxx.com",
to: "xxxx@outlook.com",
subject: "Message title",
html,
};
transporter.verify(function (error, success) {
if (error) {
console.log(error);
} else {
console.log("Server is ready to take our messages");
tos.forEach((to) => {
transporter.sendMail({ ...message, to });
});
}
});
至此,以上就是我目前探索出的比较舒服的HTML Emai的开发流程了。
最后记录一些遇到的兼容性问题:
1、邮箱字符串会被邮件客户端自动转换成a标签,并添加样式。
解决方案:
添加如下class(注:@media all中的样式会以style的形式存在在邮件中,所以最终还得看邮件客户端支不支持head里放style,www.campaignmonitor.com/css/style-e…这个网页就是用来干这个的。)
在线编辑器: 导出的html:
支持head里放style的邮件客户端最终效果(outlook、gmail、hotmail都支持): 126邮箱失败:
2、class的命名和邮件客户端内部的命名重复,导致样式错误。
qq邮箱内部有.btn
这个样式:
解决方案:修改自定义的class命名 最终效果: