为了减少首页的请求数量,按照以往的思路,会直接将 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
不久前,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
。
<?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>
svg优化方面,我们可使用 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 来设定图像的尺寸,你还可以去掉width
和height
属性让代码更简洁。
<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使用时:
用单引号替换包裹属性值的双引号
编码 <
,>
,#
,和剩余的 "
(例如在文本内容中的双引号),以及其他一直的不安全 URL 字符(例如 %
)
使用双引号来分隔 data URI(<img src="">
,url("")
)
我们通过 SASS 中实现了这个算法,使整个流程变得非常简单:
@function svg-url($svg){
@if not str-index($svg,xmlns) {
$svg: str-replace($svg, '<svg','<svg xmlns="http://www.w3.org/2000/svg"');
}
$encoded:'';
$slice: 2000;
$index: 0;
$loops: ceil(str-length($svg)/$slice);
@for $i from 1 through $loops {
$chunk: str-slice($svg, $index, $index + $slice - 1);
$chunk: str-replace($chunk, '%', '%25');
$chunk: str-replace($chunk, '"', '%22');
$chunk: str-replace($chunk, "'", '%27');
$chunk: str-replace($chunk, '&', '%26');
$chunk: str-replace($chunk, '#', '%23');
$chunk: str-replace($chunk, '{', '%7B');
$chunk: str-replace($chunk, '}', '%7D');
$chunk: str-replace($chunk, '<', '%3C');
$chunk: str-replace($chunk, '>', '%3E');
$encoded: #{$encoded}#{$chunk};
$index: $index + $slice;
}
@return url("data:image/svg+xml,#{$encoded}");
}
@mixin background-svg($svg){
background-image: svg-url($svg);
}
@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
@return if($index,
str-slice($string, 1, $index - 1) + $replace +
str-replace(str-slice($string, $index +
str-length($search)), $search, $replace),
$string);
}
以上就是如何得到能够在 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 以上的浏览器中都能够完美显示。
参考资料: