SVG 优化的边界:什么该删,什么碰一下就会坏
设计工具导出的 SVG 通常有两种体积:你看到的那张图,和文件里真实存在的东西。
一张 800 字节的图标,能把 500 多字节花在没人要的元数据上。但清理时必须清楚哪些是垃圾、哪些是命脉。
可以安全删掉的
| 内容 | 例子 |
|---|---|
| XML 声明 | <?xml version="1.0"?> |
| 注释 | <!-- Generator: Adobe Illustrator 27.0.0 --> |
| DOCTYPE | 大多数情况下与渲染无关 |
| 元数据块 | <metadata> 里塞的 RDF/DC 版权信息 |
| 编辑器专用元素 | <sodipodi:namedview inkscape:zoom="1.5"> |
| 编辑器专用属性 | inkscape:label、sodipodi:role |
| 空组 | <g></g> |
| 标签之间的空白 | 压缩后渲染结果完全相同 |
这类东西删掉后,浏览器渲染的画面一个像素都不会变。
绝对不能动的
id和所有引用:<use href="#a">、url(#grad)、clip-path、filter、mask,删掉 id 就是白板。xlink:href:老格式的引用,保留原样最稳。<defs>里的内容:看着"没被渲染",其实全是资源。viewBox的数值语义:精度可以降,但比例不能变。
所以我只删「确定与渲染无关」的东西,绝不重写路径数据 —— 那属于另一类优化(曲线拟合),风险高得多。
一个真实踩到的坑:删属性不够,得删元素
第一版实现里,我用正则删掉了 inkscape: / sodipodi: 前缀的属性,以为干净了。结果优化后的文件里还留着 <sodipodi:namedview/> 这个元素 —— 它没有子内容,但带着一堆属性,占了几百字节。
于是又补了两条规则:先整块删 <sodipodi:xxx/> 自闭合元素,再删成对出现的 <sodipodi:xxx>...</sodipodi:xxx>。
这提醒了一件事:正则处理 XML 时,属性和元素是两个维度,很容易只想到一个。
精度:全局替换的副作用
把小数位从 6 位压到 2 位,通常能省 10%–20%。但直接对全文跑一条 \d+\.\d+ 的正则,会把不该动的东西也改掉,比如 version="1.1" 变成 version="1"。
这类属性对渲染没影响,但语义变了。更稳的做法是只处理几何相关属性(d、cx、cy、r、x、y、width、height、transform),代价是要解析标签结构,代码会长不少。
我目前选了「全局替换」这条捷径,并在界面上把精度做成可选项(0–4 位),让用户自己判断。这是明确的取舍,不是疏忽。
顺带一句:精度设成 0 时,圆会变成多边形,插画边角会明显折线。图标一般 1–2 位就够了。
实测效果
拿一个带注释、metadata、namedview 的 120×120 测试图标:
原始 853 B
优化后 285 B (-67%)
验证方式是:输出的字符串仍以 <svg 开头、xlink 命名空间保留、id 和引用完好,再用浏览器渲染预览对照 —— 不是只看体积变小就完事。
什么时候别用这类工具
如果 SVG 里嵌了栅格图(<image> base64),或者用了大量 <text>,优化收益会很有限,而误改的风险更高。这种文件更适合手工整理,而不是丢进工具一把梭。
