你如何在Javascript中缓存图像

Posted

技术标签:

【中文标题】你如何在Javascript中缓存图像【英文标题】:How do you cache an image in Javascript 【发布时间】:2012-05-01 16:16:09 【问题描述】:

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

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

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

如果包含示例和/或指向进一步描述此内容的页面的链接,将不胜感激。

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

【问题讨论】:

你没有。浏览器会。 浏览器会自动缓存图片吗? @Logan:是的,浏览器会自动缓存图像,前提是您的服务器会发送必要的标头来告诉浏览器缓存它是安全的。 (这些标头也可能会告诉浏览器到期时间,这是您问题的一部分。) 如何验证我的浏览器是否发送了必要的标头? @LoganBesecker 您可以在浏览器开发工具的网络部分检查响应标头。 【参考方案1】:

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

因此,要预缓存图像,您只需将它们加载到浏览器中即可。如果您想预先缓存一堆图像,最好使用 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 - 但要确定浏览器实际上会下载并缓存每张图片,您必须等到一张图片下载完成后才能设置新的 .src 属性。否则浏览器可能会停止先前的下载并且永远不会成功缓存它。 它似乎可以在较新版本的浏览器上运行而无需等待,但你说得很好。我将不得不在一些较旧的浏览器上试用它以查看它的兼容性:) @cadlac - 不需要。当图像完成加载时,我更新了代码以从列表中删除 Image() 元素。等到图片加载完成更安全。 我添加了另一个版本的脚本,它会等待其他文档资源被加载,因此图像的预加载不会与可见页面资源的加载竞争带宽。【参考方案2】:

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

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

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

【讨论】:

是否可以在一个页面上预加载图像(不显示),然后在下一页显示? 据我所知,如果您在一个页面上预加载图像,它将被输入到浏览器缓存中,然后在任何其他页面上显示得更快。如果这是错误的,请有人纠正我。【参考方案3】:

添加完整的答案:预加载 html

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

存在其他预加载功能,但没有一个比 &lt;link rel="preload"&gt; 更适合:

&lt;link rel="prefetch"&gt;在浏览器中支持很久了, 但它旨在预取将在 下一个导航/页面加载(例如,当您转到下一页时)。这 很好,但对当前页面没有用!此外,浏览器 将赋予预取资源比预加载资源更低的优先级—— 当前页面比下一个页面更重要。有关详细信息,请参阅Link prefetching FAQ。 &lt;link rel="prerender"&gt; 呈现指定的 后台网页,如果用户导航,则加快其加载速度 给它。由于可能会浪费用户带宽,Chrome 将 prerender 视为 NoState 预取。 &lt;link rel="subresource"&gt; 不久前在 Chrome 中得到支持,并且是 打算解决与预加载相同的问题,但它有一个问题: 没有办法确定项目的优先级(因为没有 当时存在),所以它们都以相当低的优先级被获取。

市面上有许多基于脚本的资源加载器,但它们对浏览器的获取优先级队列没有任何控制权,并且会遇到大致相同的性能问题。

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

【讨论】:

【参考方案4】:

现在,有一个新的technique suggested by google 可以缓存和改进您的图像渲染过程:

    包含 JavaScript Lazysizes 文件:lazysizes.js 将文件添加到要使用的 html 文件中: &lt;script src="lazysizes.min.js" async&gt;&lt;/script&gt;lazyload 类添加到您的图像中: &lt;img data-src="images/flower3.png" class="lazyload" alt=""&gt;

【讨论】:

当我们在第二折中加载图像时这很好,即使我们添加了这个,谷歌(pagespeed.web.dev)建议缓存图像,这是不可能的商业网站(bigcommerce)我们有什么工作吗? ? @Developer 老实说我不知道​​,从未与电子商务网站合作过 没问题.. 谢谢:-)【参考方案5】:

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

&lt;link rel="prefetch" href="(url)"&gt;

更多关于支持的信息: http://caniuse.com/#search=prefetch

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

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

【讨论】:

不错不错不错不错!【参考方案6】:

你可以看看以下几点:

预加载您的图片 在 .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 请求会降低带宽,而其他人则说“感知”加载更好。我会把它留在空中。

【讨论】:

【参考方案7】:

我有一个 similar answer 用于通过 JS 异步预加载图像。动态加载它们与正常加载它们相同。他们会缓存。

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

【讨论】:

【参考方案8】:

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

我的例子:

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

这些图片有唯一的 url,每当我修改它们时,它们就会发生变化,因此它们会获得缓存标头,这些标头将在浏览器中缓存一年。

max-age: 31536000, public

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

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

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

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

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

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

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

【讨论】:

【参考方案9】:

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

但是,您可以将图像缓存设置为过期。查看这个 Stack Overflow 问题和答案:

Cache Expiration On Static Images

【讨论】:

【参考方案10】:

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

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

【讨论】:

以上是关于你如何在Javascript中缓存图像的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript入门

如何在Ionic中为缓存清除添加哈希到图像和其他静态资源?

JavaScript中的手动文件缓存

如何使用 Javascript 捕获 Flash Player 的事件

webpack loader自定义编写

webpack loader自定义编写