【译】在 dataURI 中使用 SVG 的最佳方法 _友好速搭

友好速搭

【译】在 dataURI 中使用 SVG 的最佳方法

分类: 电商搭建 | 发表时间:2016.01.06 11:47
Aaron.Z
·
-
电商搭建 2016.01.06 11:47

我搭新版官网的开发过程中,设计师提出一个需求,官网首页的头部要是一个梯形。

我们通过给头部增加一个与背景同色的 SVG 遮罩,来实现这个效果。为了减少首页的请求数量,我们按照以往的思路,直接将 SVG 转换为 base64 后插入了 CSS 文件中。当时的代码是这样的:

.svg {
    background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjUwOCIgaGVpZ2h0PSIyNTIuNyIgdmlld0JveD0iMCAwIDI1MDggMjUyLjciPjxwb2x5Z29uIHBvaW50cz0iNCwyNTIuNyAyNTA0LDAgMjUwNCwyNTIuNyIgc3R5bGU9ImZpbGw6I2U2ZWJlYSIgLz48L3N2Zz4=');
}

初步开发完成后,老大要求进一步优化代码,在查询资料时读到了这篇文章:Optimizing SVGs in data URIs。参考文章内容进行优化之后:

.svg {
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1'%3E%3Cpolygon points='4,252.7 2504,0 2504,252.7' style='fill:%23e6ebea'/%3E%3C/svg%3E");
}

对比这两段代码,最明显的效果是代码量减少了一半多,而且代码非常清晰,几乎就是 SVG 代码原文,日后如果有一些细微的需求变更,比如更改填充色,可以直接在代码里修改无需进行编码转换。

具体细节请看译文:

在 dataURI 中使用 SVG 的最佳方法 (Optimizing SVGs in data URIs)

原文链接:http://codepen.io/Tigt/post/optimizing-svgs-in-data-uris

不久前,CSS-Tricks 发表了 "Probably Don't Base64 SVG",得出结论:如果你在 data URI 中直接使用 SVG,数据量会比转化成 base64 编码格式时小。

这个观点是正确的,但是这里还有一些复杂的地方以及可优化的空间。

更好的浏览器兼容性

例如下面这段代码:

.bg {
    background: url('data:image/svg+xml;utf8,<svg ...> ... </svg>');
}

在那些流行于 web 开发者中的浏览器中是有效的,但是在 IE 中则无法正常工作。因为从技术角度来说这是一种畸形的 data URI,而 IE 很严格(原文: This is because technically it's a malformed data URI, and IE is being strict.)。

RFC 2397 定义了 data URI:

URL 的形式:

data:[<mediatype>][;base64],<data>

<mediatype> 描述数据的 MIME 类型,;base64 的出现意味着数据被编码成 base64 格式。如果没有声明;base64,对于 URL 安全字符使用 ASCII 编码,而安全范围以外的字符则使用十六进制数编码为%xx格式。如果省略<mediatype>,默认为text/plain;charset=US-ASCII

换句话说,根据标准,只有如下两种编码 data URI 的方法是有效的:

  1. data:mime/type;base64,[actual data]:base64 编码,更适合于二进制数据(PNG,fonts,SVGZ 等等)
  2. data:mime/type;charset=[charset],[actual data]:URL 编码的普通文本,更适合与文本标记语言(SVG,HTML等)

所以,把一个 SVG 文件编码为 data URL 的正确方式为 data:image/svg+xml;charset=utf8,[actual data]。我猜大部分浏览器对是否存在charset=字符串比较宽容,但是在 IE 浏览器里是必须的。为了代码的最大兼容性(例如一些小众浏览器,邮件客户端,等等),它应该被包含在内。

但这并不是全部。记得这段话么?

如果没有声明;base64,对于 URL 安全字符使用 ASCII 编码,而安全范围以外的字符则使用十六进制数编码为%xx格式。如果省略<mediatype>,默认为text/plain;charset=US-ASCII

这段话指明,要么进行 base64 编码,或者使用%格式编码非 URL 安全字符。“URL 安全字符”有一点难以理解,所以我将举一些例子。我将会使用 Chris 在他的测试中使用过的例子:

<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by IcoMoon.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 512 512"><g id="icomoon-ignore">
</g>
<path d="M224 387.814v124.186l-192-192 192-192v126.912c223.375 5.24 213.794-151.896 156.931-254.912 140.355 151.707 110.55 394.785-156.931 387.814z"></path>
</svg>

根据 Chris 的建议,我们使用 SVGO 来优化我们的 SVG 文件(如果你更习惯图形界面,GUI 版本:SCGOMG)。结果是这样的:


<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path d="M224 387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z"/></svg>

文件小了很多!而且如果你打算用 CSS 来设定图像的尺寸,你还可以去掉 widthheight 属性让代码更简洁。


<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M224 387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z"/></svg>

现在,我们把精简后的 SVG 丢进URL 编码器,会得到这样的东西:


%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%3Cpath%20d%3D%22M224%20387.814V512L32%20320l192-192v126.912C447.375%20260.152%20437.794%20103.016%20380.93%200%20521.287%20151.707%20491.48%20394.785%20224%20387.814z%22%2F%3E%3C%2Fsvg%3E

目前这是唯一能在 IE 中工作的版本。非常明显,这甚至比 base64 编码过后的 SVG 都要长:


PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMjI0IDM4Ny44MTRWNTEyTDMyIDMyMGwxOTItMTkydjEyNi45MTJDNDQ3LjM3NSAyNjAuMTUyIDQzNy43OTQgMTAzLjAxNiAzODAuOTMgMCA1MjEuMjg3IDE1MS43MDcgNDkxLjQ4IDM5NC43ODUgMjI0IDM4Ny44MTR6Ii8

“引号是关键”

你可能注意到了,Chris 使用单引号(')来界定 data URIs。这是因为他的 SVG 文件未编码时使用双引号(")来包裹属性值,为了避免冲突而使用了单引号来代替。这一点点微小的改变其实是真正精简 data URI 的关键。

"' 都是有效的属性分隔符(即:attribute="value"attribute='value' 都有效),但是只有 ' 可以直接在 URL 中使用而无须编码转换。现在我们替换双引号,编码 <> ,得到:


%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M224 387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z'/%3E%3C/svg%3E

所以,当你把 SVG 作为 data URI使用时:

  1. 用单引号替换包裹属性值的双引号
  2. 编码 <>#,和剩余的 " (例如在文本内容中的双引号),以及其他一直的不安全 URL 字符(例如 %
  3. 使用双引号来分隔 data URI(<img src="">url("")

网友 jakob-e 在 SASS 中实现了这个算法,使整个流程变得非常简单:

以上就是如何得到能够在 IE (以及标准)中使用最精简的 data URI。总结一下:

base64 编码


data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMjI0IDM4Ny44MTRWNTEyTDMyIDMyMGwxOTItMTkydjEyNi45MTJDNDQ3LjM3NSAyNjAuMTUyIDQzNy43OTQgMTAzLjAxNiAzODAuOTMgMCA1MjEuMjg3IDE1MS43MDcgNDkxLjQ4IDM5NC43ODUgMjI0IDM4Ny44MTR6Ii8+PC9zdmc+

完全 URL 编码


data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%3Cpath%20d%3D%22M224%20387.814V512L32%20320l192-192v126.912C447.375%20260.152%20437.794%20103.016%20380.93%200%20521.287%20151.707%20491.48%20394.785%20224%20387.814z%22%2F%3E%3C%2Fsvg%3E

最大程度优化的 URL 编码


data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M224%20387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z'/%3E%3C/svg%3E

我使用下面这段代码测试跨浏览器兼容性,结果它在 IE9+ 以及 安卓3.x 以上的浏览器中都能够完美显示。

分享文章
免费领取15天试用
立即注册
联系客服
微信咨询
微信二维码

领取免费试用资格

姓名 *

电话 *

公司名称

所在地区

意向产品

提交

提交成功

你好, XXX女士/先生 ,你的需求已提交成功,后续会有专门的客户经理与你电话联系。谢谢!