javascript预加载图片、css、js的方法示例介绍


Posted in Javascript onOctober 14, 2013

预加载的好处可以让网页更快的呈现给用户,缺点就是可能会增加无用的请求(但图片、css、js这些静态文件可以被缓存),如果用户访问的页面里面的css、js、图片被预加载了,用户打开页面的速度会快很多,提升用户体验。在用到一些大图片展示的时候,预加载大图是很不错的方法,图片更快的被呈现给用户。不多说了,作为一个前端攻城师都懂的,下面分享我做的测试和得到的结果。

先说需要知道的服务器返回的status code:
status-code: 200 - 客户端请求成功
status-code: 304 - 文件已经在浏览器缓存中,服务器告诉客户端,原来缓冲的文档还可以继续使用。
本文测试判断文件被是否被缓存,用的就是判断是否返回304.

下面针对预加载的几个方法,在不同的浏览器下加载img/js/css做个测试,主要包括new Image()、object、iframe。以下加载测试的js、css、图片文件,是从几个门户网站找的(为啥找几个?是为了尽可能滴测试到特殊的情况,测试中还真遇到了)。
1、测试用new Image()预加载
1.1、new Image()加载

    new Image().src = 'http://img02.taobaocdn.com/tps/i2/T1iQhUXnxpXXXXXXXX-171-48.png'; //淘宝
    new Image().src = 'http://static.paipaiimg.com/module/logo/logo_2011_02_22.png'; //拍拍
    new Image().src = 'http://co.youa.baidu.com/picture/services/images/logo.png'; //有啊
    new Image().src = 'http://img1.t.sinajs.cn/t35/style/images/common/header/logoNew_nocache.png'; //新浪*/

然后再把图片添加到页面内:<img src="xxx" />

加载图片没啥好说的,IE6-9/CM/FF/OP/都返回304,预加载成功。
1.2、测试用new Image()加载css

    new Image().src = 'http://a.tbcdn.cn/p/global/1.0/global-min.css'; //淘宝(1)
    new Image().src = 'http://static.paipaiimg.com/member/activate.css'; //拍拍(2)
    new Image().src = 'http://co.youa.baidu.com/picture/services/base.css'; //有啊(3)
    new Image().src = 'http://img1.t.sinajs.cn/t35/skin/skin_008/skin.css'; //新浪(4)
    // http://auto.sina.com.cn/css/newstyles.css
    // 可以用这个测试IE下Expires设置的时间小于当前时间的情况

再把css添加到页面内

这个有区别了:
CM/OP,都返回304(无论有没有设置Expires)。
FF, 全部返回了200。
IE,1/2/4都返回304,而3返回200。对比返回的HTTP-Header可以发现:1/2/4都设置了Expires过期时间,而3没有设置。
说明IE下缓存需要设置Expires(并且设置的时间要大于当前时间),而FF不支持利用new Image()预加载。
1.3、测试用new Image()加载js

    new Image().src = 'http://a.tbcdn.cn/s/kissy/1.1.6/kissy-min.js'; //淘宝(1)
    new Image().src = 'http://static.paipaiimg.com/js/pp.noticeBoard.js'; //拍拍(2)
    new Image().src = 'http://co.youa.baidu.com/picture/services/cms_core.js'; //有啊(3)
    new Image().src = 'http://js.t.sinajs.cn/t35/miniblog/static/js/top.js'; //新浪(4)
    new Image().src = 'http://shop.qq.com/act/static/week/fri/bang/day_1_p_0_10.js'; //QQ(5)

再把js添加到页面内。

CM/OP,都返回304
FF,只有5返回了304,也只有5的HTTP-Header最简单(包括:Date、Server、Expires、Cache-Control)。
另外几个的响应头信息内容都比较多,但也都设置了5里面的这几个。找规律,发现另外几个的响应头都有:Content-Type:text/javascript,而5里面没这个。
IE,2/5返回了304,1/3/4返回200,对比响应头,应该还是Content-Type影响的,IE里面2/5都没看到Content-Type。
另外,感谢AndrewZhang(http://www.cnblogs.com/AndyWithPassion/)提到,IE下 image 预加载js在httpwatch下查看content,资源的加载并不是完整的。我这里测试也是如此。貌似有字节限制,测试中2返回的是完整的,5有一部分内容丢失了。所以用new Image加载JS一点都不可取。

这里总结下:预加载图片用new Image()兼容性没问题。但是css/js只有OP/CM可以,IE/FF基本是无效(这点IE/FF到挺有默契)。
2、测试用object预加载

    var doc = document,
            obj = doc.createElement('object');
            //obj.data = '123.js'; //Ps: 这样写OP下无效(会把data的内容作为object标签里的text node)
            //obj.setAttribute('data', '123.js'); // img、css、js
            obj.style.cssText = 'position:absolute;top:-1px;width:1px;height:1px;';
            // obj.style.width = obj.style.height = 0;
            doc.body.appendChild(obj); // 插入object 标签需要插入到非head部分,以触发加载*/
            //obj.onload = function(){ alert('loaded') }; // FF/OP/Webkit支持(如果data是图片,IE9也可以)

然后再吧object里面data加载的文件,创建标签加到HTML内测试。

测试结果:
FF/OP/CM: 无论是img/js/css,都返回304。
IE6-8:用object加载img/js/css,会直接Aborted。
IE9比较特殊:
IE9加载js/css,先请求并返回HTTP200,再请求并Aborted,这里实际上是请求1次(第2次Aborted了)。
IE9加载img的情况,先请求并返回HTTP200,再请求返回图片,所以图片需要请求2次。

IE9的第1次请求返回的内容是空的(并且此时浏览器一般会卡住,或者直接失去响应)。 IE9首先会请求url,获取文件类型,判断是JS/CSS就Aborted,判断是图片才加载。

至于IE9第1次请求,大概是靠读取HTTP头信息来得到文件类型,或者偷偷把文件下载下来,然后在沙盒里面测试文件类型。
一个有意思的事情,比如用object加载JS,IE9有时也能加载进来,也就是第1个请求没判断出文件是JS(想看到这个要看运气了,貌似网速慢的时候可能发生)

据说以前IE是靠文件后缀来判断文件类型的,后期用HTTP头信息来判断,而他们都可以伪造,所以object在IE下存在安全问题。
IE6/7,如果文件后缀后缀为.js/.css不会发出请求,如果改成http://xxx/test.js?123.png,就发送请求了,然后用script标签引入,发现可被缓存(css这样搞也OK^^)。
IE8,后缀为js/css也不会发出请求,改后缀为png可以发出请求并得到内容,然后页面创建标签引入,文件并没有被缓存。但如果文件是真正的图片就被缓存了。
题外话:通过上面可以发现,随着IE的升级,安全性也越来越高了。

So,这里的结论是:FF/OP/CM下可以用object预加载,IE就千万别用了。
3、测试用iframe预加载

先创建页面a.html,然后加上下面的js。

    var doc = document,
            ifm = doc.createElement("iframe");
            //ifm.id="preLoadIfm";
            // ifm.style.border = ifm.width = ifm.height = 0;
            ifm.style.cssText = 'position:absolute;top:-10px;border:0;width:1px;height:1px;';
            ifm.scrolling = "no";
            doc.body.appendChild(ifm);
    window.onload = function(){ // 预加载当然最好是window.onload之后触发
            //要触发onload,需要先appendChild,然后再写onload(如果顺序颠倒,IE下不能触发)
            // ifm.onload = function(){ alert('ifm loaded'); }
            // contentWindow.document-所有都支持,contentDocument-IE9/FF/OP/CM支持
            var ifmDoc = ifm.contentDocument || ifm.contentWindow.document;
            ifmDoc.open();
            ifmDoc.write('<!doctype><html><head></head><body>');
            //ifmDoc.write('<style>html{background:#000;color:#fff}</style>'); // 用于测试
            //ifmDoc.write('<script>alert("a")<\/script>'); // 用于测试
            //ifmDoc.write('<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>');// 用于测试
            // 开始加载
            ifmDoc.write('<link rel="stylesheet" href="http://localhost/123.css?2011" />');
            ifmDoc.write('<script defer src="http://localhost/123.js?2011"><\/script>'); //不加defer,你会发现IE卡死。。
            ifmDoc.write('<img width="1" height="1" src="http://localhost/123.png?2011" />');
            ifmDoc.write('</body></html>');
            ifmDoc.close();
    };

然后创建新页面b.html,把要上面预加载的文件加到html里面,测试是否已经预加载。
结果:IE/FF/OP/CM都成功预加载。

需要说明的是:当打开a.html后,再刷新页面后,iframe内加载文件的情况。
FF,返回200(注意,这个200不是服务器返回的200,是请求缓存成功。因为发送请求的时间显示的是0)。
CM,显示状态是(from cache).
OP,虽然显示状态是n/a,但是也是from cache。 IE,IE自带的调试工具显示304,HttpWatch显示from cache。

测试环境:
WIN7 EN SP1:OP 11.50、IE7-9、FF 3.6/6.0、Chrome 10
XP EN SP3:IE6
XP EN SP3:IE7
XP CN SP3:IE8
工具:IE9自带的调试工具、HttpWatch、firebug、chrome自带的调试工具、Opera Dragonfly。

最后得出的结论:js预加载图片使用new Image()基本够用了。但是css、js特殊一些,使用object需要判断浏览器。如果考虑到js、css、img都能兼容实现预加载,可以考虑使用iframe。

另外,上面的方法创建iframe后,不使用write()写入要加载的文件,直接设置iframe.src = "cache.html",然后把要预加载的文件写在cache.html内也是可行的(以前看过有文章介绍新浪微博是这样做的,但是文章地址找不到了,搜索也没搜到),cache的网址我收藏了:http://tjs.sjs.sinajs.cn/miniblog2/static/html/cache.html,但是看微博的首页没找到这个,不知道在哪个页使用的。
其他预加载的一点补充

doc.createElement('script') 可以预加载js,如果js里面有对页面的操作,就会对页面产生影响。
doc.createElement('link') 可以预加载css,但是对当前页面的样式也可能会有影响。
所以这样预加载不太可取。
用ajax加载img/js/css,兼容性不错,文件可以被缓存,但是只能限制同域,所以使用范围有限。
预加载图片还可以利用CSS的背景图片实现。牛人lifesinger之前写过关于图片的HTTP请求的文章,不过他博客以前的数据没了。网上搜索到一篇:https://3water.com/web/110275.html。 文章里面提到了用背景图和隐藏的img标签来预加载,调理很清晰。也可以作为参考。

另外,模仿新浪的cache.html自己写了个,如果喜欢把iframe作为独立文件使用的可以作为参考。

    <!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
    <script>
    //usage: cache.html?v=123
    var win = window,
            doc = document,
            head = doc.getElementsByTagName("head")[0],
            getQuery = function(){
                    var ret = {},
                            sch = win.location.search,
                            arr,
                            tmp;
                    if (sch) {
                            sch = sch.substr(1);
                            arr = sch.split("&");
                            for(var i = 0, j = arr.length; i < j; i++) {
                                    tmp = arr[i].split('=');
                                    ret[tmp[0]] = tmp[1];
                            }
                    }
                    return ret;
            },
            version = getQuery().v || '';
    win.onerror = function(){return true}; //屏蔽js错误提示
    win.onload = function(){
            var b = doc.createElement("script");
            b.src = 'http://xx/1.js?v=' + version;
            head.appendChild(b);
            //...
    };
    doc.write('<link rel="stylesheet" href="http://xxx/3.css?version=' + version + '" \/>');
    </script>
    <img src="http://xxx/4.png" />
    </body></html>
Javascript 相关文章推荐
JavaScript 对话框和状态栏使用说明
Oct 25 Javascript
jquery验证手机号码、邮箱格式是否正确示例代码
Jul 28 Javascript
setInterval()和setTimeout()的用法和区别示例介绍
Nov 17 Javascript
js输入框邮箱自动提示功能代码实现
Dec 10 Javascript
js打造数组转json函数
Jan 14 Javascript
利用jQuery和CSS将背景图片拉伸
Oct 16 Javascript
深入分析Javascript事件代理
Jan 30 Javascript
vue watch自动检测数据变化实时渲染的方法
Jan 16 Javascript
如何把vuejs打包出来的文件整合到springboot里
Jul 26 Javascript
js脚本中执行java后台代码方法解析
Oct 11 Javascript
JavaScript实现矩形块大小任意缩放
Aug 25 Javascript
vant picker+popup 自定义三级联动案例
Nov 04 Javascript
js获取或设置当前窗口url参数的小例子
Oct 14 #Javascript
几种延迟加载JS代码的方法加快网页的访问速度
Oct 12 #Javascript
JavaScript如何从listbox里同时删除多个项目
Oct 12 #Javascript
javascript动态的改变IFrame的高度实现自动伸展
Oct 12 #Javascript
JavaScript var声明变量背后的原理示例解析
Oct 12 #Javascript
原生js操作checkbox用document.getElementById实现
Oct 12 #Javascript
基于jquery的9行js轻松实现tab控件示例
Oct 12 #Javascript
You might like
php cookies中删除的一般赋值方法
2011/05/07 PHP
php array的学习笔记
2012/05/16 PHP
PHP SPL标准库之数据结构堆(SplHeap)简单使用实例
2015/05/12 PHP
php超快高效率统计大文件行数
2015/07/05 PHP
Laravel框架实现利用中间件进行操作日志记录功能
2018/06/06 PHP
PHP copy函数使用案例代码解析
2020/09/01 PHP
修改jQuery Validation里默认的验证方法
2012/02/14 Javascript
Extjs4中的分页应用结合前后台
2013/12/13 Javascript
Jquery $.getJSON 在IE下的缓存问题解决方法
2014/10/10 Javascript
如何使用jquery修改css中带有!important的样式属性
2016/04/28 Javascript
jQuery插件扩展extend的简单实现原理
2016/06/24 Javascript
AngularJS 视图详解及示例代码
2016/08/17 Javascript
详解JS异步加载的三种方式
2017/03/07 Javascript
BootStrap数据表格实例代码
2017/09/13 Javascript
JS匿名函数内部this指向问题详析
2019/05/10 Javascript
Fetch超时设置与终止请求详解
2019/05/18 Javascript
借助云开发实现小程序短信验证码的发送
2020/01/06 Javascript
[54:09]RNG vs Liquid 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.23
2019/09/05 DOTA
一个检测OpenSSL心脏出血漏洞的Python脚本分享
2014/04/10 Python
python MysqlDb模块安装及其使用详解
2018/02/23 Python
django项目搭建与Session使用详解
2018/10/10 Python
python模拟登陆,用session维持回话的实例
2018/12/27 Python
PYTHON实现SIGN签名的过程解析
2019/10/28 Python
Python散点图与折线图绘制过程解析
2019/11/30 Python
在python3中实现查找数组中最接近与某值的元素操作
2020/02/29 Python
Django中使用Json返回数据的实现方法
2020/06/03 Python
Python应用实现双指数函数及拟合代码实例
2020/06/19 Python
python 无损批量压缩图片(支持保留图片信息)的示例
2020/09/22 Python
用Python写一个for循环的例子
2016/07/19 面试题
工商管理本科毕业生求职信范文
2013/10/05 职场文书
自我评价个人范文
2013/12/16 职场文书
党员服务承诺书
2014/05/28 职场文书
2014办公室副主任四风对照检查材料思想汇报
2014/09/20 职场文书
不尊敬老师的检讨书
2014/12/21 职场文书
主题班会开场白
2015/06/01 职场文书
python树莓派通过队列实现进程交互的程序分析
2021/07/04 Python