【问题标题】:Post-loading : check if an image is in the browser cache加载后:检查图像是否在浏览器缓存中
【发布时间】:2011-01-27 15:39:42
【问题描述】:

短版问题: 是否有适用于所有浏览器的 navigator.mozIsLocallyAvailable 等效功能或替代方法?

加长版:)

嗨, 这是我的情况: 我想为 asp.net MVC 实现一个 HtmlHelper 扩展,可以轻松处理图像后加载(使用 jQuery)。

所以我使用“alt”属性中指定的源来渲染带有空图像源的页面。 我在“window.onload”事件之后插入图片源,效果很好。

我做了这样的事情:

$(window).bind('load', function() {
    var plImages = $(".postLoad");
    plImages.each(function() {
        $(this).attr("src", $(this).attr("alt"));
    });
});

问题是:第一次加载后,加载后的图片会被缓存。但是如果页面加载需要 10 秒,则缓存后加载的图像将在这 10 秒后显示。

如果图像被缓存以立即显示它们,我认为在“document.ready”事件中指定图像源。

我找到了这个函数:navigator.mozIsLocallyAvailable 来检查图像是否在缓存中。这是我对 jquery 所做的:

//specify cached image sources on dom ready
$(document).ready(function() {
    var plImages = $(".postLoad");
    plImages.each(function() {
        var source = $(this).attr("alt")
        var disponible = navigator.mozIsLocallyAvailable(source, true);
        if (disponible)
            $(this).attr("src", source);
    });
});

//specify uncached image sources after page loading
$(window).bind('load', function() {
        var plImages = $(".postLoad");
        plImages.each(function() {
        if ($(this).attr("src") == "")
            $(this).attr("src", $(this).attr("alt"));
    });
});

它适用于 Mozilla 的 DOM,但不适用于任何其他 DOM。我试过 navigator.isLocallyAvailable :同样的结果。

还有其他选择吗?

【问题讨论】:

    标签: javascript jquery caching lazy-loading


    【解决方案1】:

    经过一番研究,我找到了解决方案:

    这个想法是记录缓存的图像,在图像“加载”事件上绑定一个日志函数。 我首先想到将源存储在cookie中,但是如果没有cookie就清除缓存是不可靠的。此外,它还向 HTTP 请求添加了一个 cookie...

    然后我遇到了魔法:window.localStorage (details)

    localStorage 属性提供 域的持久存储区域

    正是我想要的:)。这个属性在 HTML5 中是标准化的,它已经适用于几乎所有最新的浏览器(FF、Opera、Safari、IE8、Chrome)。

    代码如下(不处理window.localStorage不兼容的浏览器):

    var storage = window.localStorage;
    if (!storage.cachedElements) {
        storage.cachedElements = "";
    }
    
    function logCache(source) {
        if (storage.cachedElements.indexOf(source, 0) < 0) {
            if (storage.cachedElements != "") 
                storage.cachedElements += ";";
            storage.cachedElements += source;
        }
    }
    
    function cached(source) {
        return (storage.cachedElements.indexOf(source, 0) >= 0);
    }
    
    var plImages;
    
    //On DOM Ready
    $(document).ready(function() {
        plImages = $(".postLoad");
    
        //log cached images
        plImages.bind('load', function() {
            logCache($(this).attr("src"));
        });
    
        //display cached images
        plImages.each(function() {
            var source = $(this).attr("alt")
            if (cached(source))
                $(this).attr("src", source);
        });
    });
    
    //After page loading
    $(window).bind('load', function() {
        //display uncached images
        plImages.each(function() {
            if ($(this).attr("src") == "")
                $(this).attr("src", $(this).attr("alt"));
        });
    });
    

    【讨论】:

    • 我认为这会在缓存过期时中断...您需要将响应标头中的过期日期(这将需要 ajax 调用)存储在 localStorage 中。例如localStorage[src] = expires 在 logCache 中,now &lt; localStorage[src] 在缓存中。 (显然需要更多代码;但你明白了。)
    • 重新考虑这可能不是一个大问题,因为我们正在谈论图像......
    【解决方案2】:

    检查图像是否已被缓存的最有效、最简单且受广泛支持的方法是执行以下操作...

    1. 创建图像对象
    2. 将 src 属性设置为所需的 url
    3. 立即检查完成属性,看看图像是否已经缓存
    4. 将src属性设置回""(空字符串),这样就不会不必要地加载图片(除非你此时想加载粗略)

    就这样……

    function isCached(src) {
        const img = new Image();
        img.src = src;
        const complete = img.complete;
        img.src = "";
        return complete;
    }
    

    在你的情况下,它可以像这样实现......

    const lazyImages = document.querySelectorAll(".postLoad");
    for (const img of lazyImages) {
        if ((!img.src || !isCached(img.src)) && img.getAttribute("alt")) {
            img.src = img.getAttribute("alt");
        }
    }
    

    话虽如此,我建议您不要为此使用 alt 属性,您应该改用 data-src 之类的东西。

    【讨论】:

      【解决方案3】:

      如果图像被缓存,对图像的 ajax 请求几乎会立即返回。然后使用 setTimeout 确定它是否未准备好并取消请求,以便您可以重新排队以备后用。

      更新:

      var lqueue = [];
      $(function() {
        var t,ac=0;
        (t = $("img")).each(
          function(i,e)
          {
            var rq = $.ajax(
            {
              cache: true,
              type: "GET",
              async:true,
              url:e.alt,
              success: function() { var rq3=rq; if (rq3.readyState==4) { e.src=e.alt; } },
              error: function() { e.src=e.alt; }
            });
      
            setTimeout(function()
            {
              var k=i,e2=e,r2=rq;
              if (r2.readyState != 4)
              {
                r2.abort();
                lqueue.push(e2);
              }
              if (t.length==(++ac)) loadRequeue();
            }, 0);
          }
        );
      });
      
      function loadRequeue()
      {
        for(var j = 0; j < lqueue.length; j++) lqueue[j].src=lqueue[j].alt;
      }
      

      【讨论】:

      • 感谢您的有趣回答。我已经对其进行了测试,但是有一个问题:(在图像完全加载之前,立即触发了成功回调。我尝试用“完成”替换“成功”事件,同样的事情。我认为成功/complete 事件不会等待加载响应的组件来触发。
      • 无论如何,问题是这个解决方案将 HTTP 请求数加倍。我想避免这种情况。
      【解决方案4】:

      我对您的空图像源有意见。你写道:

      所以我使用“alt”属性中指定的源来渲染带有空图像源的页面。我在“window.onload”事件之后插入图片源,效果很好。

      过去我遇到过这个问题,因为在某些浏览器中,空的src 属性会导致额外的请求。这是他们所做的(从Yahoo! performance rules 复制,还有一个blog post 对此问题有更详细的说明):

      • Internet Explorer 向页面所在的目录发出请求。
      • Safari 和 Chrome 向实际页面本身发出请求。
      • Firefox 3 及更早版本的行为与 Safari 和 Chrome 相同,但 3.5 版解决了此问题 [错误 444931] 并且不再发送请求。
      • 遇到空图像 src 时,Opera 不执行任何操作。

      我们在我们的网站上也使用了很多 jQuery,但并非总是可以避免空图像标签。我选择使用 1x1 像素的透明 gif,如下所示:src="t.gif" 用于仅在页面加载后插入的图像。它非常小并且被浏览器缓存。这对我们来说非常有效。

      干杯,奥利弗

      【讨论】:

      • 嗨,奥利弗,你是对的。我使用的另一种解决方案是设置与图像相同的宽度和高度的跨度。此外,HTML5 有自定义属性,所以使用它: 然后,脚本使用自定义属性信息将整个跨度替换为图像。问候,马修
      【解决方案5】:

      以防其他人遇到同样的问题。此处提供的一些解决方案(即将缓存信息存储在本地浏览器数据存储中)可能会因两个原因而中断。首先如果图像的缓存过期,其次如果缓存被用户清除。另一种方法是将图像源设置为占位符。然后将源更改为图像路径/名称。这样,浏览器就有责任检查自己的缓存。应该适用于大多数浏览器,无论它们的 API 是什么。

      【讨论】:

        【解决方案6】:

        在 2017 年,Resource Timing API 可以帮助您使用 PerformanceResourceTiming.transferSize 属性检查这一点。从服务器下载(未缓存)时,此属性应返回非零传输大小,如果从本地缓存中获取,则返回零。

        参考:https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/transferSize

        【讨论】:

        • 现在是 2020 年,Safari 和 IE 仍然需要支持 transferSize 属性,因此这不适用于企业解决方案。
        【解决方案7】:

        对于任何可能尝试使用 React 解决此问题的人,我使用 complete 图像属性在 React 中以这种方式解决它:

        import React, { useState, useEffect, useRef } from 'react'
        
        const Component= () => {
          const [isLoadedImage, setLoadedImage] = useState(false)
          const imageRef = useRef(null)
        
          useEffect(() => {
            const imgEl = imageRef.current
            if (imgEl && imgEl.complete && !isLoadedImage) setLoadedImage(true)
          })
        
          return (
            <img
              onLoad={() => (!isLoadedImage ? setLoadedImage(true) : null)}
              ref={imageRef}
            />
          )
        }
        

        【讨论】:

          猜你喜欢
          • 2013-04-14
          • 2013-03-08
          • 2016-12-18
          • 2016-01-05
          • 2014-08-07
          • 1970-01-01
          • 1970-01-01
          • 2019-09-30
          • 1970-01-01
          相关资源
          最近更新 更多