[翻译]只为图片使用IMG标签(Use IMG tags only for Images)

原文地址:Use IMG tags only for Images

首先,补充一些背景知识。

web开发人员经常通过在主页预加载(预缓存)将来的页面所用到的一些资源的方式来优化网站的性能。常用的手段是在主页内容加载完之后开启预读取资源下载。有些网站通过使用IMG标签的形式来打到预读取的目的----IMG没有同源的限制(limited
to same-origin
requests
),这使它看起来是一种理想的选择。如果通过IMG标签来预读取css文件或者js文件,这个文件将只会被加载,但不会被解析和执行。

在有些网站中,尤其是一些前沿(cutting-edge )网站中,他们尝试通过在页面很靠前的位置使用隐藏的IMG标签指向同一个页面后面将会使用的资源的方式来“帮助”浏览器的先行下载(Lookahead
Downloaders
)。通过这种方式,这些资源可以优先下载,当页面解析执行到真正使用这些资源的地方时,这些资源就可以在缓存中直接读取。这种方式实际上就是让浏览器早点进行资源下载。当这种方式必须要在页面的最开始(接近最开始,head,或者接近head的地方)使用,而且应该距离使用这些资源的script、link标签很远才能达到与读取的目的。

不幸的是,使用这种方式来预读取js和css文件实际上会拖慢整个页面。

我深爱着一个神秘的工具(Fiddler),因此当一个网站拥有者邮件告诉我,他使用Fiddler的时候发现,IE浏览器在同一个页面加载同一个资源两次,尽管第二个下载是在第一次下载完成之后才开始,而且第一次下载的资源已经带着一个可以让资源复用的设置了缓存的响应头时我会很兴奋。这到底是怎么回事呢?

幸好,他给我的信息是在 X-Download-Initiator
header
开启的情况下捕捉的,这样的话我就可以看到每次请求是如何产生的。第一次的脚本下载是在先行下载(浏览器自身策略)到达IMG标签的时候开始的。第二个请求是解析到使用同一个src的script标签时开始的。这两个URL是相同的,那为什么第二次请求还是会发送呢?

经过更深入的查看发现,原来第一个请求实际上是被浏览器取消了!这也解释了第二个请求会发送的原因(第一个请求根本没发出去,资源没有被加载)。为什么第一个请求会被取消呢?

当IE遇到IMG标签时,它就会创建一个image对象,并且分配给一个下载请求。当请求的资源数据开始到达时,数据就会流入浏览器的图片解码器。如果数据是文本的话,解码器就会把数据当成异常数据并拒绝执行。这看上去很是合理:解码器不可能利用这些数据。当解码器把数据当成“不可能是图片(Not
possibly an
image)”的异常时,前面创建的image对象就会取消它的过程。如果资源还没有下载完毕,也会被当成过程的一部分被取消。

取消一个下载对于性能非常不利。首先,浏览器不会得到请求的资源----只有在取消前已经下载完的部分资源可以被缓存,甚至这一部分只有在响应中带上ETAG和content-length信息的时候才能被缓存。如果这两个response头信息都有的话,浏览器才可能在后面通过HTTP
Range request请求下载资源的剩余部分。  
其次,建立TCP/IP连接是非常昂贵的(也许在这前面还有HTTPS的握手过程),抛弃建立好的连接会明显的增加页面的加载时间。

我制作了一个叫MeddlerScript 的小工具,这个工具可以清楚地展示这个问题。在 Meddler中加载这个ms文件,然后点击 Meddler上的编译和查看按钮。你的浏览器将会打开一个会加载两个js文件的html页面,一个用于IMG标签,另一个用于script标签。script标签使用的js资源请求会像预期的那样立即执行,而IMG标签使用的JS资源请I去也会像预期的那样不会执行。如果你点击第二个HTML页面上的链接,script标签拉取的脚本会直接从缓存中读取和使用,而IMG标签拉取的脚本则会从服务器重新下载。查看Meddler的log,会看到如下信息:

Script loaded at 7:15:26.  Ready for
connections. 
0: GET /; 
1: GET
/796-SCRIPTPretendingToBeAImg.js; 
2: GET
/796-SCRIPTNOTPretendingToBeAImg.js; 
1:
Error: An established connection was aborted by the software in your host
machine 
3: GET
/UseTheScript.htm; 
4: GET
/796-SCRIPTPretendingToBeAImg.js; Range: bytes=4237-; If-Range:
"796";

黄底行显示通过IMG标签拉取JS资源的下载会被取消和关闭TCP连接。然后在session#4中可以看到,在第二个页面中,浏览器会发送一个HTTP请求去获取脚本的剩余数据。

如果使用Fiddler模拟这个脚本,必须开启Streaming
Mode
才能看到log信息。这是因为Fiddler默认会完全缓冲每个响应,并交付给浏览器一个快照,这意味着image对象不可能被取消,除非下载已经完成和缓存。这也重申了通过IMG预读取资源会成功的情况只发生在网络状况很好的时候。

相同的取消行为发生在IE6-IE10和Firefox 12.0中,Opera 11.61, Chrome 18,
和Safari 5.1.5看起来在image内容不合法的情况下也不会取消请求。

有意思的是,Firefox好像也不会缓存部分的响应数据;当脚本再次下载的时候,请求不会带上Range头。这种行为也许可以解释,如果Firefox对于图片请求和其他标签采取分离的缓存(某种我相信Mozilla在一些地方使用的缓存结构)。这个观点(缓存分离)还有更深入的证据支持。如果更新(刷新)MeddlerScript让第一个请求可以快速的完成,让浏览器根本没机会取消,Firefox还是会在第二个页面中重新下载脚本文件。

Script loaded at 7:31:39.  Ready for
connections. 
0: GET /; 
1: GET
/143-SCRIPTPretendingToBeAImg.js; 
2: GET
/143-SCRIPTNOTPretendingToBeAImg.js; 
3: GET
/usethescript.htm; 
4:
GET /143-SCRIPTPretendingToBeAImg.js;

当web开发者遇到这个问题来请教我有没有替代方案时,我首先想到的是尝试使用IE的startDownload方法来容纳他们的脚本,即使这个方法有同源策略的限制。不幸的是,startDownload方法并不是一个合适的替代品。通过startDownload开启的下载的创建是包含no-cache的,就会导致创建请求时缓存是不会被读取的,而且响应也不会被缓存。

HTML5提供了一个明确的方式(prefetch
Link relation
)允许浏览器预读取资源。IE9以上(注:chrome、firefox也都支持)支持通过link来进行DNS-prefetching(DNS预读取);这些资源并不会被下载。

-Eric

PS:通过IMG标签来预读取图片当然是没有问题的......只要你不是在HTTPS页面上请求HTTP域的图片。这样做的话(在HTTPS页面上请求HTTP的图片)会引起内容错乱的问题( Mixed-Content
problem
),而且你页面的锁定icon将会消失。

(文章后面有几条评论也很不错,一起翻译了)

igrigorik(web性能权威指南的作者)

5 May 2012 2:40 PM

Eric, 很棒的深入研究! 有点好奇,
IE对于在object标签上的类似情况(通过object加载css、js资源)是如何处理的?
理论上来说,通过object标签做预读取应该可以避免取消(终止)解析。说了这么多,向你指出的那样, 预读取hack就是......(不可能实现)。
我希望有些事我们可以远离(something I hope we can migrate away from)。

EricLaw
[ex-MSFT]

7 May 2012 11:24 AM

@igrigorik:
看起来我不能得到一个不会引起相同问题的OBJECT标签(最少在标准模式下)。可能有些参数的组合可以起到作用,但我不知道是哪种组合。

Mathieu ‘p01‘ Henri

20 May 2012 6:33 AM

通过IMG标签来实现这种方式真的很不好。我更倾向于使用object标签。

@EricLaw, 你试过<object type="text/plain" data="scriptNeededLater.js"
></object> 吗? 这种方式不需要伪装就可以用于各种资源。这可以看作是一种通用内容类型的同一资源。

EricLaw [ex-MSFT]

20 May 2012 6:43 AM

@Mathieu: 你可以修改MeddlerScript来测试这种方式. 不幸的是,你会发现第一个页面只产生了一个HEAD 请求,
然后确定了object的MIME类型不是准备渲染的类型,然后就停止了。你应该想的更远一些,确保资源自身是text/plain的内容类型,但及时确保text/plain内容类型有用也不建议使用,因为(当从服务端返回的头部信息中设置了X-Content-Type-Options:
nosniff,且请求的url是通过script标签引用的方式时,如果返回的Content-Type类型不是以下之一["text/javascript",
"application/javascript", "text/ecmascript", "application/ecmascript",
"text/x-javascript", "application/x-javascript", "text/jscript",
"text/vbscript", "text/vbs"]的话,IE9下是不会加载请求的js文件的。参考文章:通过动态添加script标签请求数据(JSONP)的一些问题)如果响应头部信息包括X-Content-Type-Options:
nosniff指令时整个过程就会中断。而且还会造成缓存清理的混乱(js相对于其他类型被清理的情况要轻得多)。

chrisbro

22 Jan 2014 3:26 PM

Eric, 我对于你在igrigorik的回复中的否定抱有疑问。 你的回答应该可以理解为 我也不能让Object标签起作用",是吧?
 我们通过使用Mathieu说的<object type="text/plain" data="scriptNeededLater.js"
></object>做了一个页面,这个页面会预加载js文件(后面的独立页面会用到)。IE10会做HEAD请求,IE10也会发起get请求----但是Fiddler显示这个请求被“取消”了,而且这个资源并没有实际缓存起来。很难判断IE10到底下载了几个文件。

EricLaw [ex-MSFT]

22
Jan 2014 3:37 PM

我直接下结论吧: 不要使用Object标签,这个是没用的。 对于IE11+(chrome、firefox), 使用<link
rel="prefetch">.

Radko Dinev

6 Feb 2014 5:29 AM

@EricLaw:
如你所说,如果头部信息包含Content-Length和ETag,即使是部分数据也会被缓存。如果用Last-Modified代替ETag呢,
效果是不是也应该是一样的呢?

[EricLaw]:
至少在IE9, 继续请求下载部分数据后的剩余数据需要相应中包含ETAG(requires
that the response contain a strong ETAG
);只包含Last-Modified
date的响应是不会被继续获取的。

后记:本来想在业务中使用controljs的,但是看到这篇文章,再想到IE的占有份额,谁还敢用?相对于资源下载,还是TCP连接保持更重要。还有,这篇文章只提到了js的问题,CSS呢?有时间的话自己研究一下。

参考文章:

Thoughts on script loaders

preload css javascript without execution

ControlJS介绍

用ControlJS优化阿里妈妈广告

http://stevesouders.com/controljs/

https://groups.google.com/forum/#!forum/controljs

[翻译]只为图片使用IMG标签(Use IMG tags only for Images),布布扣,bubuko.com

时间: 2024-10-07 15:57:21

[翻译]只为图片使用IMG标签(Use IMG tags only for Images)的相关文章

img只显示图片一部分 或 css设置背景图片只显示图片指定区域

17:14 2016/3/22img只显示图片一部分 或 css设置背景图片只显示图片指定区域 background-position: 100% 56%; 设置背景图片显示图片的哪个坐标区域,图片左上角为0,0或0%,0%,右下角为高度和宽度,或100%,100%. clip:rect(300px 100px 300px 0px); 设置显示图片的某个区域,分别是上右下左的顺序设置 部分代码:<style type="text/css">img {position:abs

js点击图片删除子标签li

上图的效果图请看下面 点击图片部分会删除子标签li部分

HTML:图片和视频标签的使用

介绍:在html网页中,图片和视频是基本的元素,在网页中插入图片和视频有自己的标签,分别是img.embed,来源都是使用src来链接. 插入图片: <img src="图片的来源" alt="图片的介绍" title="鼠标放在图片时显示的信息"> 使用alt:是为了方便搜索引擎识别图片,因为基本上搜索引擎是不能直接识别二进制的图片的. 使用title:是为了方便用户鼠标触碰图片查看一些相关的信息. 插入视频: <embed

【Javascript】IE8兼容 背景图片与a标签的onclick事件

先说几句牢骚话. 虽然IE8比之IE6.7有很大的进步,但是在执行效率.兼容性上仍然有很多问题.被广大开发者喜爱的平台才是好平台. 可惜多亏当年盗版XP打开中国的计算机市场,IE作为一款捆绑软件仍然在中国有很大的使用人群.既然是中国人自己埋下的坑,咬着牙也要走下去. a标签的onclick事件 在开发现在这个平台时,用的是国产dwz开源前端框架,还是挺好用的,对IE的兼容性做的也比较好,很少发现有啥大问题. 只是如开头标题所示,页面弹出提醒框时,[确定]按钮点不了,导致页面相当于死机一样,只能重

图片与超链接标签

1.图片标签 1.在网页中插入一张图片 2.语法:<img> 3.属性: (1): src:指定要显示的图片路径 (2): width:设置图片的宽度,以像素px为单位,也可以省略单位 (3): height:设置图片的高度 (4): title:用来设置图片的标题,当鼠标悬停在图片上方是出现 (5): alt:用来设置图片加载失败之后的提示文本 2.超链接标签 1.语法:<a>超链接内容</a> 标签属性:herf:必填属性,指定链接地址,当属性为空 "&q

PHP不改变图片长宽只改变图片体积大小的压缩方法

现在基本上很多网站都有图片管理,包括企业站也是,很多图片为了保证质量可能需要高清,但是图片太大的话,对于服务器也是个压力,每次加载页面的时候光页面图片就得好长时间,影响用户体验. 当然,如果有钱你可以 1.cdn静态资源包 2.oss管理文件资源,不一定是图片,也可以是文件哦 3.搭建自己的文件服务器,图片当然可以了 不过以上好是好,不过都得花钱,我感觉还是从根本上解决比较好,来吧,那我们就上代码把 <?php /** * 图片压缩类:通过缩放来压缩. * 如果要保持源图比例,把参数$perce

京东不删差评,只删图片?

前段时间在京东上搞了张桌子,过程不是那么愉快,于是给了个差评并附了图片.今天偶然上去看了一下,发现图片不见了,于是和客服较真了一下,不过最后依然败在客服妹子手里. ?

从数据库读取二进制图片,img标签显示图片

引自 http://www.w3dev.cn/article/20110214/asp-net-csharp-image-base64-change.aspx      <img src="@myPicc.ImageInfo" type="image/jpeg" /> 直接给src图片的二进制是不允许的,但是支持base64字符串形式,在后台转化二进制为base64string格式传给前台. data:images/gif;base64,"转化

DEDECMS首页,列表页调用自定义图片字段,只显示图片地址

第一步:将自定义字段“图片”类型改为“图片(仅地址)”类型. 第二部:在{dede:arclist row='1' addfields='stu' titlelen='24' orderby='pubdate' typeid='35' channelid='1' } 中添加一句listtype='image'就可以解决问题了.