【ArchSummit 】架构顶层设计视角梳理从0到1打造大模型体系的路径! 了解详情
写点什么

前端遗留技术与现代功能的对抗,邮件开发注定是件苦差事

作者 | s0.rumi

  • 2023-09-11
    北京
  • 本文字数:4529 字

    阅读完需:约 15 分钟

大小:2.30M时长:13:23
前端遗留技术与现代功能的对抗,邮件开发注定是件苦差事

首先,如果大家点进来的原因是厌烦了开发邮件系统,请允许我先对各位的悲惨遭遇表达最诚挚的慰问。

 

说说结论,我认为邮件系统的开发可以说是能在笔记本电脑上完成的、最恶心的工作,没有之一。我们做的一切似乎都没有意义,只能像疯子一样反复测试一切,那种感觉跟清理浴室地板上莫名其妙的顽固污渍倒有几分相似。

 

总之,希望文章接下来的内容能帮大家厘清整个混乱的局面,提供一点有用的建议,特别是让您在绝望中找到一丝活下去的勇气。

 

邮件开发是干啥的?

 

理论上讲,邮件系统的开发其实跟网站开发应该比较相似。电子邮件在本质上只是个 HTML 文档,跟网页一样,只不过是在邮件客户端、面非网络浏览器中呈现视觉效果。但除此之外,二者都能渲染,也就是把 HTML 代码转换成文本、图形和图像——即内容的可视化。

 

其实在 2005 年那会,网站和邮件系统的开发其实非常相似。浏览器和邮件客户端会以几乎相同的方式呈现 HTML,而且功能也相差不大。但是,尽管 Web 标准不断发展且持续入驻网络浏览器,但邮件客户端这头却似乎陷入了时停——至今无甚变化。

 

如今,我们在开发网站时可以支持各种酷炫且高效的功能,比如网格、Flexbox、夜晚模式、过渡,而且所有主要浏览器都能兼容。但另一方面,这些功能在邮件客户端中则分以下三种情况:

  1. 完全不受支持;

  2. 无法按预期工作;

  3. 在某些邮件客户端中无法兼容。

 

所以,如果大家希望一定比例的用户(至少得有 95%吧)能按预期查看邮件内容,那就只能坚持使用最基本的 HTML 和 CSS 功能。而且即使这样,成功率也不是 100%……

 

而且更离奇的是,如今 Web 开发中最糟糕的实践竟然仍是邮件开发中的最佳实践。下面,就让我们一探究竟。

 

首先,如果大家点进来的原因是厌烦了开发邮件系统,请允许我先对各位的悲惨遭遇表达最诚挚的慰问。

说说结论,我认为邮件系统的开发可以说是能在笔记本电脑上完成的、最恶心的工作,没有之一。我们做的一切似乎都没有意义,只能像疯子一样反复测试一切,那种感觉跟清理浴室地板上莫名其妙的顽固污渍倒有几分相似。

总之,希望文章接下来的内容能帮大家厘清整个混乱的局面,提供一点有用的建议,特别是让您在绝望中找到一丝活下去的勇气。

 

为什么要用<table> 元素?

 

邮件开发最让人头痛,当数其中大量使用到 table 元素,以及永无止境的<tr>和<td>字符串。但是,为什么会这样?

 

根据相关文献的解释,微软 Outlook 使用着与 Word 相同的渲染引擎。也就是说,在 Outlook 中打开电子邮件基本上相当于在 Word 中打开文档,所以我们得先摆正思路——手头开发的并不是电子邮件,而是 Word 文档。

 

有朋友可能会想,“不会吧,Word 里可没有多少布局和样式工具……”说得没错!但它有 table,而且只有 table。所以任何想要正确实现可视化的内容都必须是 table。没有其他办法了,请大家收下这份表格大礼。

 

为了证明这一点,以下是苹果发送的现代电子邮件被粘贴进微软 Word 2013 后的样子:

 


微软 Word 2013 中打开的苹果发票邮件

 

神奇吧,这格式多么规整。而之所以能这么规整,是因为邮件的 HTML 中包含 75 个<tr>和 122 个<td>。看看 HTML 格式,就知道内容有多乱了。

 

为什么要使用内联样式?


跟常规 HTML 文档一样,电子邮件也可以具有 CSS 样式。如果各位朋友足够理智,肯定会想到把它们放在文档的<head>标记当中。根据“如何开发邮件……”支持页面中的<link>和<style>部分的说明,这种处理方式能让样式得到良好渲染。

 

我们可以选择“正确的方式”,也就是发送邮件、打开邮件,然后发现它的呈现效果跟预期一致。但问题是用户不只会接收邮件,还会撰写自己的邮件,甚至进一步再做转发。

 

那在转发电子邮件时,具体会发生什么?根据 Stack Overflow 上的回答,简单来讲,<head>中的所有内容都会被删除。就是说我们向其中添加的任何新式,都会被 Gmail 无情抛弃。

 

唯一不会被删除的样式就只有内联样式。因此,如果希望电子邮件在转发之后仍然正常显示,那就只能使用内联样式。

 

以下是我转发的苹果通知邮件:



在 Gmail 中渲染得到的转发邮件

 

看着没什么毛病,对吧?那是因为其中用到了 40 个内联样式属性。不信?大家可以看看这封邮件的 HTML 代码证明我没说谎:

https://dodov.dev/blog/why-does-email-development-have-to-suck/email-inline-styles.html

 

下面我们删掉内联样式,看看更新之后的 HTML。在浏览器端,二者的显示效果几乎相同,因为内联样式所提供的样式会被复制到<head>当中作为后备。但因为转发邮件时这些样式会被删除,所以我们的样式就彻底消失了:



Gmail 中渲染的、不带内联样式的转发邮件

 

可以看到,标题、页脚、间距全都是一团糟……这显然不对劲,但至少还有个合乎逻辑的理由——保障安全。电子邮件客户端在渲染 HTML 之前,会对其进行预处理以保证安全,样式也是这样被丢掉的。

 

如果大家希望自己的邮件在转发时看着能有点章法,那就必须拿起内联样式的“颜料瓶”冲着 CSS 之墙拼命喷洒。

 

颜色反转

 

在开发网站的时候,我们会用 Prefers-Scheme 来检测用户是否在 DAMB 模式下查看,并相应更改当前页面的调色板。您猜怎么着?大多数电子邮件客户端还不支持这项功能。时间已经过去了 20 年,Apple Mail 等少数客户端倒是支持,但 Gmail 却采用了另一种不同的方法……

 

在谷歌看来,一切问题说到底都是概率论问题。只要在数学上具备可行性,那就可以完全不管少数情况下的怪异效果,这就免去了重新设计调色板和其他颜色的麻烦。

 

所以在夜晚模式下,Gmail 会简单将邮件中的所有颜色反转——包括背景、边框和文本颜色,如下图所示:



iOS 版本的 Gmail 客户端,会在夜晚模式时直接将颜色反转

 

可悲的是,这事我们防不胜防、几乎没办法做预先控制。唯一的办法就是尽量拣选那些在反转之后效果仍然不错的配色,保证图像在常规和反转配色时都有过得去的观感……这事不容易,大家多留点时间吧。

 

全宽内容

 

在移动设备上,我们可能需要让内容从一端显示到另一端,正常的网站也都是这么显示的。大多数移动邮件客户端也都支持这种方案,除了……Gmail。

 

Gmail 在每封邮件的侧面,都放置了一块莫名其妙的 16 像素空白。

 


Apple Mail 和 Gmail 的侧边留白比较

 

我们没法去掉这块留白。查看边距?已经是 0 了。填充?是 0。而且!important 已经全部应用过了。反正就是解决不了,你既检测不到它、也没法做进一步处理。忍着吧,强迫症们!


自定义字体

 

对组织来说,品牌中最重要的组成部分应该就是字体了吧,所以我们当然想在邮件中也继续使用自己的独特字体……可以吗?行啊,除了 Gmail。

 

大多数电子邮件客户端都不支持 font-face 字体,但这却是 Gmail 那边使用率最高的字体。

Stack Overflow 发帖有云,这时候只能使用设备操作系统提供的本地字体。总之,希望各位的品牌多跟 Arial 和 Times New Roman 合作!🤞

 

响应式图像

 

有时候,我们可能需要张台式机壁纸,又想把同样的画面也放到移动设备端。假设大家已经读过 MDN 的响应式图像指南,就会想到这时应该使用 srcset……没错,只是邮件客户端这边不支持。

 

为了解决这个问题,我们需要使用多个<img>元素,然后使用媒体查询把它们隐藏掉。但如果稍不注意,这里也有陷阱:

 

 

@media (max-width: 600px) and (prefers-color-scheme: dark) {/* only show on mobile in dark mode */.something { display: block }}
复制代码

 

这里我们只能倒转逻辑,使用两个单独的媒体查询,并依靠 CSS 级联来覆盖掉之前的样式:

/* always show… */.something { display: block }/* …but hide on desktop… */@media (min-width: 601px) {.something { display: none }}/* …and in light mode… */@media (prefers-color-scheme: light) {.something { display: none }}
复制代码

 

大家可能感受得到,这东西太容易出错了。

 

其他小问题


如果大家已经读过这篇文章,但仍不相信开发电子邮件有多么痛苦,那下面咱们再看点别的小例子:

  • Outlook 中没有 table 填充。所以当我们在<table>上设置 CSS 填充时,Outlook 只会对表内的所有<td>元素应用填充。但我们至少可以覆盖掉<td>元素本身的填充……

  • 大多数电子邮件客户端会扫描文本内容中的邮件地址和电话号码,然后把它们转换成看起来很丑的蓝色<a>链接形式。我们必须把它们打包进<a>标签,并提前标记再删除样式才能避免这个问题:

 

<a style="color: inherit; text-decoration: none;" href="">some@example.com</a>
复制代码

 

如果邮件地址是条指向空 href 的链接,那电子邮件客户端就不会这么处理。在 Outlook 中,列表项目还应该用边距分开,且列表本身需要缩进来保证保留边距:

 

<ul style="margin: 0 0 0 18px; padding: 0;"><li style="margin-bottom: 24px;">foo</li><li style="margin-bottom: 24px;">bar</li><li style="margin-bottom: 0;">baz</li></ul>


复制代码

 

最后一个条目须明确设置 0 边距,避免底部再留额外的空间。像这样的问题,还有很多……

 

有办法解决吗?

 

其实并没有太好的解决办法,大家别抱什么希望。所以在跟设计师合作时,一定要让他们知道邮件系统的开发有多么复杂。告诉他们关于上述问题的所有细节,提醒他们在设计时务必考虑到这些现实挑战。

 

而且即便通过协商得出了更简洁的设计,上述问题也只是得到了缓解,它们仍然存在。另外,永远别以为你可以编写“干净的代码”来让电子邮件系统始终保持整洁、正常工作。总会在一些地方,总会有一些东西就是不起作用。在邮件开发当中,我们唯一能够确定的就只有这点。

 

当然,MJML 和 React Email 等项目能帮上不少忙。它们会努力把电子邮件客户端里那些晦涩难懂的怪癖抽象出去。例如,使用 MJML,我们可以忘掉所有复杂性,让创建邮件真正变得简单:

 

<mj-column><mj-image width="64px" src="/assets/logo.png"></mj-image><mj-divider border-color="#88846f"></mj-divider><mj-text font-size="24px" color="#f8f8f2">Hello World</mj-text></mj-column>
复制代码

 

这样看着就好多了,对吧?用不着再处理一大堆<tr>和<td>,MJML 会在后台帮各位解决。总之,欢迎大家多体验体验 MJML,并参阅 Josh Comeau 的文章了解这款强大的 HTML 邮件开发工具:

https://www.joshwcomeau.com/react/wonderful-emails-with-mjml-and-mdx/

 

写在最后

 

与符合 Web 标准的网络浏览器不同,电子邮件客户端从不给任何人面子。电子邮件开发之所以很糟糕,就是因为我们在网站构建时所使用的很多现代功能在邮件这边根本不受支持。这就迫使我们只能使用遗留技术,同时需要考虑各种各样的极端情况。

 

电子邮件的构建方式跟网站不同,所以千万别像设计网站那样设计电子邮件。尽量用更简单的布局,同时配合 MJML 这类项目消除种种令人头痛的问题。各位,你们一定能挺过去!

 

最后,别觉得丢脸,没人能搞定邮件客户端……没人可以。

 

原文链接:

https://dodov.dev/blog/why-does-email-development-have-to-suck


相关阅读:

前端精准测试实践

大前端测试的思考和在语雀的实践分享

Java 后端有哪些不用学的技术?劝退。

前后端分离技术体系

2023-09-11 10:341926

评论

发布
暂无评论
发现更多内容

大模型如何助力开发者打造新质生产力

百度开发者中心

人工智能 大模型

面试官:限流的实现方式有哪些?

王磊

Java 面试

NFTScan | 02.19~02.25 NFT 市场热点汇总

NFT Research

NFT NFT\ NFTScan

数字人SaaS系统源码部署,轻松实现直播带货!

青否数字人

教你学会Next.js14(c-shopping电商开源)

Geek_9da61c

JWT Tailwind next.js joi rtk query

鸿蒙App开发该如何提速

Geek_2305a8

小程序应用、页面、组件生命周期(涵盖启动、显示、隐藏、错误、未找到、卸载阶段)

天津汇柏科技有限公司

小程序开发 软件定制开发 软件开发定制

动手实践丨轻量级目标检测与分割算法开发和部署(RK3568)

华为云开发者联盟

人工智能 自动驾驶 华为云 华为云ModelArts 华为云开发者联盟

鸿蒙应用成企业布局新方向 鸿蒙人才成开年之后“香饽饽”

最新动态

如何为数据采集项目选择合适的API

Noah

微软曾想将 Bing 搜索卖给苹果;英伟达首次公开将华为列为对手丨 RTE 开发者日报 Vol.151

声网

分库分表如何管理不同实例中几万张分片表?

程序员小富

Java 分库分表 springboot

解决混合精度训练大模型的局限性问题

百度开发者中心

人工智能 深度学习 大模型

从零开始用Rust编写nginx,原来TLS证书还可以这么申请

EquatorCoco

nginx rust 开发语言 语言 & 开发

青否数字人源码软件平台开发!

青否数字人

数字人

鸿蒙Next升级是否有便捷的方法?

Geek_2305a8

文心一言 VS 讯飞星火 VS chatgpt (202)-- 算法导论15.3 1题

福大大架构师每日一题

福大大架构师每日一题

企业过二级等保采购哪家堡垒机好?电话多少?

行云管家

等保 堡垒机 等保测评 二级等保

提升生产能力的必备工具——MES系统自动排产

万界星空科技

生产管理系统 mes 万界星空科技 生产管理 自动排产

大模型时代软件研发效率革命

百度开发者中心

人工智能 大模型

能碳双控| AIRIOT智慧能碳管理解决方案

AIRIOT

智慧系统 能碳管理 智慧能碳

AI数字人SadTalker实战

不在线第一只蜗牛

AI 数字人 人工智能技术

前端遗留技术与现代功能的对抗,邮件开发注定是件苦差事_架构/框架_InfoQ精选文章