如何在Javascript中缓存图像

How do you cache an image in Javascript

本文关键字:缓存 图像 Javascript      更新时间:2023-09-26

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

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

另外,是否可以设置映像的缓存版本何时过期?

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

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

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

因此,要预缓存图像,您所要做的就是将它们加载到浏览器中。 如果你想预缓存一堆图像,最好用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);
正如

@Pointy所说,你不会用javascript缓存图像,浏览器会这样做。 所以这可能是你所要求的,也可能不是......但是您可以使用JavaScript预加载图像。通过将要预加载的所有图像放入数组并将该数组中的所有图像放入隐藏的 img 元素中,可以有效地预加载(或缓存)图像。

var images = [
'/path/to/image1.png',
'/path/to/image2.png'
];
$(images).each(function() {
var image = $('<img />').attr('src', this);
});

为答案的完整性而添加:使用 HTML 预加载

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

存在其他预加载功能,但没有一个像<link rel="preload">那样适合用途:

  • <link rel="prefetch">在浏览器中已经支持了很长时间,但它旨在预取将在下一个导航/页面加载(例如,当您转到下一页时)。这很好,但对当前页面没有用!此外,浏览器将使预取资源的优先级低于预加载资源 —当前页面比下一页更重要。请参阅链路预取常见问题了解更多详情。
  • <link rel="prerender">呈现指定的网页在后台,如果用户导航,则加快其加载速度到它。由于有可能浪费用户带宽,Chrome将预呈现视为 NoState 预取。
  • 不久前,Chrome 支持<link rel="subresource">,并且旨在解决与预加载相同的问题,但它有一个问题:没有办法确定项目的优先级(因为没有当时存在),所以它们都以相当低的优先级被获取。

有许多基于脚本的资源加载器,但它们对浏览器的获取优先级队列没有任何权力,并且受到大致相同的性能问题的影响。

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

现在,谷歌提出了一种新技术来缓存和改进您的图像渲染过程:

  1. 包括 JavaScript Lazysize 文件:lazysizes.js
  2. 将文件添加到要在以下位置中使用的 html 文件: <script src="lazysizes.min.js" async></script>
  3. lazyload类添加到映像: <img data-src="images/flower3.png" class="lazyload" alt="">

您可以查看以下几点:

预加载图像
在 .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请求占用了带宽,而另一些人则说"感知"加载更好。 我会把它留在空中。

即使您的问题说"使用 javascript",您也可以使用 link 标签的 prefetch 属性来预加载任何资产。在撰写本文时(2016 年 8 月 10 日),它在 Safari 中不受支持,但几乎在其他任何地方都受支持:

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

有关支持的更多信息,请单击此处:http://caniuse.com/#search=prefetch

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

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

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

我的例子:

我的主页上有一个旋转横幅,上面有 4 张图片,滑块等待 2 秒,然后 javascript 加载下一张图像,等待 2 秒,等等。

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

max-age: 31536000, public

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

现在,当我进行常规刷新或转到子页面并单击返回时,缓存中的图像似乎被忽略了。我希望在Chrome开发工具的"网络"选项卡中看到"来自磁盘缓存"的灰色消息。相反,我看到请求每两秒通过一次绿色状态圆圈而不是灰色,我看到正在传输数据,所以我得到的印象是根本没有从 javascript 访问缓存。它只是在每次加载页面时获取图像。

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

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

如果这是Chrome Devtools中的一个错误,那真的让我感到惊讶,没有人注意到这一点。 ;)

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

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

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

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

是的,浏览器会自动为您缓存图像。

但是,您可以将映像缓存设置为过期。查看此堆栈溢出问题并回答:

静态映像上的缓存过期

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

  1. 您需要将图像 URL 列表作为对象或数组,例如:
var sources = {
    lion: '/assets/lion.png',
    monkey: '/assets/monkey.png'
};
    定义
  1. 函数定义,其中它接收图像 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. 最后,您需要调用该函数。您可以使用
$(document).ready(function () {
    loadImages(sources, buildStage);
});