我写过使用内联 <svg>
图标可以实现 最佳图标系统。我认为这仍然是正确的。这是将图标添加到页面上的最简单方法。没有网络请求,可以完美地进行样式设置。
但是内联代码有一些缺点,其中之一是它没有利用缓存。当您在页面之间浏览时,浏览器会一遍又一遍地读取和处理相同的代码。这并不是什么大问题。还有更多需要关注的性能问题,对吧?但考虑更有效的模式仍然很有趣。
Scott Jehl 写道 即使您内联某些内容,也不意味着您不能将其缓存。让我们看看 Scott 的想法是否可以扩展到 SVG 图标。
从内联 SVG 开始
像这样…
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline SVG</title>
<link rel="stylesheet" href="/styles/style.css">
</head>
<body>
...
<svg width="24" height="24" viewBox="0 0 24 24" class="icon icon-alarm" xmlns="http://www.w3.org/2000/svg">
<path id="icon-alarm" d="M11.5,22C11.64,22 11.77,22 11.9,21.96C12.55,21.82 13.09,21.38 13.34,20.78C13.44,20.54 13.5,20.27 13.5,20H9.5A2,2 0 0,0 11.5,22M18,10.5C18,7.43 15.86,4.86 13,4.18V3.5A1.5,1.5 0 0,0 11.5,2A1.5,1.5 0 0,0 10,3.5V4.18C7.13,4.86 5,7.43 5,10.5V16L3,18V19H20V18L18,16M19.97,10H21.97C21.82,6.79 20.24,3.97 17.85,2.15L16.42,3.58C18.46,5 19.82,7.35 19.97,10M6.58,3.58L5.15,2.15C2.76,3.97 1.18,6.79 1,10H3C3.18,7.35 4.54,5 6.58,3.58Z"></path>
</svg>
将文本作为文件放入浏览器缓存非常容易
在上面的 HTML 中,选择器 .icon-alarm
将为我们获取该图标的整个 SVG 块。
const iconHTML = document.querySelector(".icon-alarm").outerHTML;
然后,我们可以将其放入浏览器的缓存中,如下所示
if ("caches" in window) {
caches.open('static').then(function(cache) {
cache.put("/icons/icon-wheelchair.svg", new Response(
iconHTML,
{ headers: {'Content-Type': 'image/svg+xml'} }
));
}
}
看到文件路径 /icons/icon-wheelchair.svg
了吗?这个路径是编造的。但它确实会放在该位置的缓存中。

让我们确保浏览器在请求该文件时将其从缓存中获取
我们将在页面上注册一个服务工作者
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
}
服务工作者本身非常小,只是一个缓存匹配器
self.addEventListener("fetch", event => {
let request = event.request;
event.respondWith(
caches.match(request).then(response => {
return response || fetch(request);
})
);
});
但是… 我们从未请求过该文件,因为我们的图标是内联的。
是的。但是,如果其他页面也从该缓存中受益呢?例如,可以像这样在页面上放置一个 SVG 图标
<svg class="icon">
<use xlink:href="/icons/icon-alarm.svg#icon-alarm" />
</svg>
由于 /icons/icon-alarm.svg
已经准备好放在缓存中,浏览器会很乐意将其从缓存中取出并显示。
(我对这个方法有效感到有点惊讶。Edge 不喜欢链接到文件的 <use>
元素,但这种情况很快就会结束。**更新**,它已经结束了,Edge 已经使用 Chromium 了。)
即使该文件不在缓存中,假设我们实际上将该文件放在文件系统上,可能是某种“包含”的结果(我在演示中使用了 Nunjucks)。
<use>
和内联 SVG 并不完全相同
但是… 是的。我喜欢上面这种方法是因为它利用了缓存,并且图标应该几乎立即呈现。您还可以通过这种方式进行一些样式设置 - 例如,为父图标设置填充应该会通过 <use>
创建的 shadow DOM 传递,并对其中的 SVG 元素进行着色。
不过,它并不完全相同。与内联 SVG 相比,shadow DOM 是一个很大的障碍。
因此,请对其进行增强!我们可以异步加载一个脚本,该脚本找到每个 SVG 图标,Ajax 请求它需要的 SVG,并替换 <use>
内容…
const icons = document.querySelectorAll("svg.icon");
icons.forEach(icon => {
const url = icon.querySelector("use").getAttribute("xlink:href"); // Might wanna look for href also
fetch(url)
.then(response => response.text())
.then(data => {
// This is probably a bit layout-thrashy. Someone smarter than me could probably fix that up.
// Replace the <svg><use></svg> with inline SVG
const newEl = document.createElement("span");
newEl.innerHTML = data;
icon.parentNode.replaceChild(newEl, icon);
// Remove the <span>s
const parent = newEl.parentNode;
while (newEl.firstChild) parent.insertBefore(newEl.firstChild, newEl);
parent.removeChild(newEl);
});
});
现在,假设这个 JavaScript 正确执行,这个页面就有了与原始页面相同的内联 SVG。
我不明白它是怎么进入缓存的?
您必须至少有一个内联的 svg 图标?
还没来得及尝试,但它的逻辑看起来很有趣,至少在出现真正的解决方案之前是如此。谢谢克里斯。
好的,所以我从这篇文章中了解到的是,使用 SVG 雪碧技术,然后使用 JS 将 SVG 的内容替换为真实的 SVG 数据。
这正是 svg4everybody 填充所做的。它旨在用作填充,但(我认为)您可以关闭填充功能,使其适用于所有浏览器。(我可能误解了文档。)
我认为我会在代码库中发布一个问题,并提出一个关于这种行为的功能请求。我不确定它目前是否按我的想法执行。
我创建了一个 功能请求。不过,看起来该项目不再维护了,因此不太可能实现。
最简单的选择可能是本地存储一份副本,并删除检查外部
<use>
浏览器支持的 if 语句。嗯,谢谢,我会试一下。我构建了一个名为 IconPress 的图标管理插件,其中我只加载内联或 AJAX 的图标雪碧。
本来希望使用外部雪碧,但由于浏览器不一致(例如无法在符号(或雪碧)中添加 CSS 样式),我放弃了,又回到了内联嵌入图标。我使用内联时遇到的问题是缓存它们。
与使用更适合网站性能的 awesome 字体相比
我还没有真正测试过图标字体和内联 SVG 图标之间的性能差异,但我认为使用图标字体更理想。
虽然内联 SVG 图标便于对图标内的单个元素进行样式设置,但它没有将表现形式与内容分离。换句话说,这些 SVG 代码仅出于样式设置目的存在于 HTML 中,有些人对此存在争议。
在我看来,如果不需要对图标内的单个元素进行样式设置(这种情况很少见),使用 CSS 伪元素 ::before 和 ::after 来呈现图标字体是一种更好的方法。
我假设您指的是 Font Awesome 之类的图标字体?它们的问题在于,大多数情况下,会加载整个字体,而不仅仅是该页面所需的图标。这意味着您加载了数百个永远不会使用的图标。使用内联 SVG,您只加载需要的内容,其他什么也不加载。这非常有利于性能,而且您还可以使用 CSS 对 SVG 的各个部分(笔划或填充!)进行样式设置,而图标字体只能用一种颜色填充形状。
它不像“直接插入”解决方案那么方便,但它更具性能优势,而且整体上更可定制。
您可以使用文档片段而不是 span 吗?
这将是自定义元素的一个很好的用例。也许?
我认为这是错误的方法。
将 SVG 内联到 CSS 中,它会自动缓存,并在启用时在服务器端压缩。
请在示例中使用
href
属性由于
xlink:href
已经被弃用,并且在几年前这里的一篇文章中也提到了这一点