如何在Javascript中缓存图像


83

我和我的朋友们在一个网站上工作,我们希望在其中缓存某些图像,以便将来更快地显示它们。我有两个主要问题:

  1. 您如何缓存图像?
  2. 图像被缓存后如何使用?(只是为了验证图像是否在页面A上缓存,可以从缓存中调用它以在页面B上使用它,对吗?)

此外,有可能设置时,图像的缓存版本将到期?

如果包括示例和/或进一步描述页面的链接,将不胜感激。

我们可以使用原始Javascript或jQuery版本。


12
你不知道 浏览器可以。
Pointy 2012年

浏览器是否会自动缓存图像?
Logan Besecker

8
@Logan:是的,只要您的服务器发送必要的标头来告知浏览器可以安全地缓存图像,浏览器就会自动缓存图像。(这些标头可能还会告诉浏览器到期时间,这是您的问题的一部分。)
icktoofay 2012年

如何验证我的浏览器正在发送必要的标头?
Logan Besecker

1
@LoganBesecker您可以在浏览器开发工具的“网络”部分中检查响应标题。
jlaceda

Answers:


131

一旦将图像以任何方式加载到浏览器中,它将保存在浏览器缓存中,并且无论该图像是在当前页面还是在其他任何页面中使用,只要下次使用该图像,它将在下一次使用时更快地加载。在浏览器缓存过期之前使用。

因此,要预缓存图像,您要做的就是将它们加载到浏览器中。如果您想预缓存一堆图像,最好使用javascript来完成,因为使用javascript处理时通常不会阻止页面加载。您可以这样做:

function preloadImages(array) {
    if (!preloadImages.list) {
        preloadImages.list = [];
    }
    var list = preloadImages.list;
    for (var i = 0; i < array.length; i++) {
        var img = new Image();
        img.onload = function() {
            var index = list.indexOf(this);
            if (index !== -1) {
                // remove image from the array once it's loaded
                // for memory consumption reasons
                list.splice(index, 1);
            }
        }
        list.push(img);
        img.src = array[i];
    }
}

preloadImages(["url1.jpg", "url2.jpg", "url3.jpg"]);

可以根据需要多次调用此函数,每次都会将更多图像添加到预缓存中。

一旦通过javascript这样预先加载了图片,浏览器就会将其保存在缓存中,您可以仅在其他位置(在您的网页中)引用常规URL,浏览器就会从缓存中获取该URL,而不是通过网络。

最终,随着时间的推移,浏览器缓存可能会填满并扔掉一段时间未使用的最古老的东西。因此,最终,图像将被冲出缓存,但它们应在缓存中停留一段时间(取决于缓存的大小以及完成了多少其他浏览)。每次实际重新加载图像或在网页中使用图像时,图像都会自动刷新它们在浏览器缓存中的位置,因此不太可能将其从缓存中清除。

浏览器缓存是跨页面的,因此它适用于加载到浏览器中的任何页面。因此,您可以在站点中的一个地方预缓存,然后浏览器缓存将对站点中的所有其他页面起作用。


当按上述方式进行预缓存时,图像将异步加载,因此它们不会阻止页面的加载或显示。但是,如果您的页面上有很多自己的图像,则这些预缓存图像可以与带宽或与页面中显示的图像竞争连接。通常,这不是一个明显的问题,但是在连接速度较慢的情况下,这种预缓存可能会减慢主页的加载速度。如果可以最后一次加载预加载图像是可以的,那么您可以使用该函数的一个版本,该版本将等待开始预加载,直到所有其他页面资源均已加载。

function preloadImages(array, waitForOtherResources, timeout) {
    var loaded = false, list = preloadImages.list, imgs = array.slice(0), t = timeout || 15*1000, timer;
    if (!preloadImages.list) {
        preloadImages.list = [];
    }
    if (!waitForOtherResources || document.readyState === 'complete') {
        loadNow();
    } else {
        window.addEventListener("load", function() {
            clearTimeout(timer);
            loadNow();
        });
        // in case window.addEventListener doesn't get called (sometimes some resource gets stuck)
        // then preload the images anyway after some timeout time
        timer = setTimeout(loadNow, t);
    }

    function loadNow() {
        if (!loaded) {
            loaded = true;
            for (var i = 0; i < imgs.length; i++) {
                var img = new Image();
                img.onload = img.onerror = img.onabort = function() {
                    var index = list.indexOf(this);
                    if (index !== -1) {
                        // remove image from the array once it's loaded
                        // for memory consumption reasons
                        list.splice(index, 1);
                    }
                }
                list.push(img);
                img.src = imgs[i];
            }
        }
    }
}

preloadImages(["url1.jpg", "url2.jpg", "url3.jpg"], true);
preloadImages(["url99.jpg", "url98.jpg"], true);

在此示例中,您将为每个URL创建一个新的Image对象。如果在循环外部设置img变量并仅更新src,则它应具有相同的效果并减少资源
cadlac

1
@cadlac-但要确定浏览器实际上会下载并缓存每个图像,您必须等到一个图像下载完成后再设置新的.src属性。否则,浏览器可能会停止先前的下载,而永远不会成功缓存它。
jfriend00 2014年

它似乎可以在较新版本的浏览器上运行,而无需等待,但您会很高兴。我必须在某些旧版浏览器上进行尝试才能看到它的兼容性:)
cadlac

@cadlac-不需要。我更新了代码,以Image()在图像加载完成后从列表中删除该元素。等待图像加载完成比较安全。
jfriend00

1
我添加了脚本的另一个版本,该版本等待加载其他文档资源,以便图像的预加载不会与可见页资源的加载竞争带宽。
jfriend00

13

正如@Pointy所说的,您不使用javascript缓存图像,浏览器可以做到这一点。因此这可能是您要的,可能不是...但是您可以使用javascript预加载图像。通过将要预加载的所有图像放入数组并将该阵列中的所有图像放入隐藏的img元素,可以有效地预加载(或缓存)图像。

var images = [
'/path/to/image1.png',
'/path/to/image2.png'
];

$(images).each(function() {
var image = $('<img />').attr('src', this);
});

2
是否可以在一页上预加载图像(不显示),然后在下一页上显示?
Logan Besecker 2012年

2
据我所知,如果您将图像预加载到一页上,它将被输入到浏览器缓存中,然后在其他任何页面上显示更快。如果这是错误的,请有人纠正我。
Trav McKinney

3

您可以看几件事:

预加载图像
在.htaccess文件中设置缓存时间
图像的文件大小并对其进行base64编码。

预加载:http//perishablepress.com/3-ways-preload-images-css-javascript-ajax/

缓存:http : //www.askapache.com/htaccess/speed-up-sites-with-htaccess-caching.html

对于base64编码,有两种不同的想法,有人认为http请求会降低带宽,而另一些人则认为“感知”的加载更好。我将其悬而未决。


3

即使您的问题是“使用javascript”,您也可以使用prefetch链接标记的属性来预加载任何资产。截至撰写本文时(2016年8月10日),Safari尚不支持该功能,但其他地方几乎都支持该功能:

<link rel="prefetch" href="(url)">

有关支持的更多信息,请参见:http : //caniuse.com/#search=prefetch

请注意,caniuse矩阵中未列出IE 9,10,因为Microsoft已停止对它们的支持。

因此,如果您真的不喜欢使用javascript,则可以使用jquery将这些元素动态添加到页面中;-)


1
不错不错不错不错!
Bhargav Nanekalva '17

2

添加答案的完整性:HTML预加载

<link rel="preload" href="bg-image-wide.png" as="image">

还存在其他预加载功能,但没有任何一个适合以下目的<link rel="preload">

  • <link rel="prefetch">已在浏览器中长期支持,但它旨在预取将在下一次导航/页面加载中使用的资源(例如,当您转到下一页时)。很好,但是对当前页面没有用!此外,浏览器将为预取资源提供比预加载资源更低的优先级-当前页面比下一页更重要。有关更多详细信息,请参见链接预取常见问题
  • <link rel="prerender">在后台呈现指定的网页,如果用户导航到该网页,则可以加快其加载速度。由于可能浪费用户带宽,因此Chrome会将预渲染视为NoState预取。
  • <link rel="subresource"> 之前在Chrome中受支持,并且旨在解决与预加载相同的问题,但是它有一个问题:无法确定项目的优先级(因为当时不存在)以较低的优先级获取。

那里有许多基于脚本的资源加载器,但是它们对浏览器的获取优先级队列没有任何作用,并且会遇到很多相同的性能问题。

来源:https : //developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content


这应该是公认的答案。谢谢!
Renan Coelho

1

对于通过JS异步预加载图像,我有类似的答案。动态加载它们与正常加载它们相同。他们将缓存。

至于缓存,您无法控制浏览器,但可以通过服务器进行设置。如果您需要按需加载真正的新资源,则可以使用缓存无效化技术来强制加载新资源。


1

我使用类似的技术来延迟加载图像,但不禁会注意到Javascript在首次加载时不会访问浏览器缓存。

我的例子:

我的首页上有一个旋转的横幅,上面有4张图片,滑块要等待2秒,然后JavaScript会加载下一张图片,请等待2秒,依此类推。

这些图像具有唯一的URL,每当我对其进行修改时,这些URL都会更改,因此它们会获得缓存头,这些头将在浏览器中缓存一年。

max-age: 31536000, public

现在,当我打开Chrome Devtools并确保禁用“禁用缓存”选项时,并首次加载页面(在清除缓存后),所有图像都将被获取并处于200状态。横幅中所有图像的完整周期后,网络请求将停止,并使用缓存的图像。

现在,当我进行常规刷新或进入子页面并单击返回时,似乎忽略了缓存中的图像。我希望在Chrome devtools的“网络”标签中看到“来自磁盘缓存”的灰色消息。取而代之的是,我看到请求每隔两秒钟以绿色状态圈(而不是灰色)通过,我看到了正在传输的数据,因此给人的印象是根本无法从javascript访问缓存。每次加载页面时,它仅获取图像。

因此,无论图像的缓存策略如何,对首页的每个请求都会触发4个请求。

考虑到上述因素以及大多数Web服务器和浏览器现在支持的新http2标准,我认为最好停止使用延迟加载,因为http2几乎同时加载所有图像。

如果这是Chrome Devtools中的错误,那真是让我还没有人注意到的惊喜。;)

如果是这样,则使用延迟加载只会增加带宽的使用。

如果我错了,请纠正我。:)



0

我总是喜欢使用Konva JS:图像事件中提到的示例来加载图像。

  1. 您需要具有图像URL列表作为对象或数组,例如:

    var sources = { lion: '/assets/lion.png', monkey: '/assets/monkey.png' };

  2. 定义函数定义,在其中接收图像URL列表并在其参数列表中接收回调函数,因此在完成图像加载后,您可以在网页上开始执行:

    function loadImages(sources, callback) {
                var images = {};
                var loadedImages = 0;
                var numImages = 0;
                for (var src in sources) {
                    numImages++;
                }
                for (var src in sources) {
                    images[src] = new Image();
                    images[src].onload = function () {
                        if (++loadedImages >= numImages) {
                            callback(images);
                        }
                    };
                    images[src].src = sources[src];
                }
            }

  1. 最后,您需要调用该函数。您可以从jQuery文档准备就绪”中 调用它

$(document).ready(function (){ loadImages(sources, buildStage); });


0

如今,谷歌建议一种新技术来缓存和改善您的图像渲染过程:

  1. 包括JavaScript Lazysizes文件:lazysizes.js
  2. 将文件添加到要在以下位置使用的html文件: <script src="lazysizes.min.js" async></script>
  3. lazyload类添加到您的图像中: <img data-src="images/flower3.png" class="lazyload" alt="">
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.