翻译自https://perishablepress.com/3-ways-preload-images-css-javascript-ajax/

让图片预加载可以很好的提升用户体验。当在浏览器里把所有的图片都预先加载好了之后,用户可以在浏览你的站点中体验到非常快的加载时间。如果你想要在图片集网站或者大量使用图片的网站里尽可能的把你的东西无缝的传递给用户,那么,这个方法对于你来说是非常有益的。

1、使用CSS和JavaScript实现预加载

预加载图片的方法有很多,包括使用CSS,JavaScript以及各种各样的其他组合的方法。作为我在Perishable Press中最喜欢的一个话题,我已经讨论过几次关于图片的预加载了:

这些技术每一个都是建立在之前的方法上的,并且,对于各种各样的设计情况都高效适用。幸运的是,读者们总是时不时的对这些文章提供意见和建议。最近Lan Dunn发表了一篇文章对我的Better Image Caching with CSS方法进行了改进。使用那个方法,可以很简单的使用CSS实现图片的预加载:

1
2
3
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }

通过一些战略性的技巧为已经存在的HTML元素添加preloadID,我们可以使用CSS的background属性来将选择的图片在屏幕之外预加载。然后,一旦这些图片的路径与网页中其他地方引用的图片的路径相同的时候,浏览器就会在渲染页面时使用这些预先加载好的(即缓存的)图片。简单,高效,并且不需要JavaScript。

尽管这是一个很高效的方法,但是,仍然有改进的空间。正如Lan Dunn所指出的,使用这个方法来预加载的图片会和页面的其他内容一起被加载,因此就增加可页面的总体加载时间。要解决这个问题,我们可以使用一点JavaScript来延迟这个预加载,直到页面加载完成。这个方法通过CSS的background属性很容易实现,使用Simon Willison的addLoadEvent()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// better image preloading @ https://perishablepress.com/press/2009/12/28/3-ways-preload-images-css-javascript-ajax/
function preloader() {
if ( document.getElementById ) {
document.getElementById('preload-01').style.background = 'url(http://domain.tld/image-01.png) no-repeat -9999px -9999px';
document.getElementById('preload-02').style.background = 'url(http://domain.tld/image-02.png) no-repeat -9999px -9999px';
document.getElementById('preload-03').style.background = 'url(http://domain.tld/image-03.png) no-repeat -9999px -9999px';
}
}
function addLoadEvent( func ) {
var oldonload = window.onload;
if ( typeof window.onload !== 'function' ) {
window.onload = func;
}
else {
window.onload = function () {
if ( oldonload ) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);

在代码的第一部分,我们通过使用background样式来设置真正的需要预加载的多个图片。这样,要使用这个方法,你就需要将preload-01之类的ID修改为你自己的页面中的标记的ID。另外,对于每一个background属性,你还需要将图片的路径替换为你自己的图片的路径。其他的就没有什么需要修改的了。

然后,在代码的第二部分,我们使用addLoadEvent()函数来延迟了preloader()函数的执行。直到页面加载完毕。

所以,如果用户的浏览器上JavaScript不可用会发什么?很简单,图片不会被预加载,只会被正常的加载出来。这是一种我们喜欢的不招摇的,优雅的降级JavaScript的使用的方法。

2、只使用JavaScript来实现预加载

尽管前面一种方法被证明是有效的,但是,我还是觉得实现起来太单调而且太花时间了。相反的,我更偏爱直接使用JavaScript来实现预加载。下面是使用JavaScript实现的两种预加载的方法,几乎所有的现代浏览器都可以运行。

方法1

不招摇,优雅降级,并且容易实现,只需要根据需要简单的添加或者移除图片的路径或者名字,不需要其他的更改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="hidden">
<script type="text/javascript">
<!--//--><![CDATA[//><!--
var images = new Array();
function preload() {
for ( i = 0; i < preload.arguments.length; i++ ) {
images[i] = new Image();
images[i].src = preload.arguments[i];
}
}
preload(
'http://domain.tld/gallery/image-001.jpg',
'http://domain.tld/gallery/image-002.jpg',
'http://domain.tld/gallery/image-003.jpg'
);
//--><!]]>
</script>
</div>

这个方法对于预加载大量图片来说尤其方便。我在我的一个作品集网站上就使用这个方法预加载了几乎50张图片。通过将这段代码引入登录页面或者内部的需要巨大开销的页面,用户们在他们获取到登陆资格的时候,大多数的图片就已经被预加载了。太棒了!

方法2

下面是一个类似的使用不明显的JavaScript来加载任意数量的图片的方法。只需要简单的将这段代码引入需要的页面,然后根据后续指导修改一下就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="hidden">
<script type="text/javascript">
<!--//--><![CDATA[//><!--
if ( document.images ) {
img1 = new Image();
img2 = new Image();
img3 = new Image();
img1.src = 'http://domain.tld/path/to/image-001.gif';
img2.src = 'http://domain.tld/path/to/image-002.gif';
img3.src = 'http://domain.tld/path/to/image-003.gif';
}
//--><!]]>
</script>
</div>

就像你看到的那样,每一个预加载的图片都有一个变量定义,img1 = new Image();以及一个资源声明img3.src = "../path/to/image-003.gif";。通过重复使用这个模式,你可以按照需要预加载任意数量的图片。希望你能明白,如果有什么不明白的地方,请在评论中说明,然后,会有人帮助你理解的。

我们甚至可以通过延迟预加载来对这个方法进行一些改进。我们只需要简单的对这些代码进行一下封装,然后使用addLoadEvent()来让他生效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function preloader() {
if ( document.images ) {
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = 'http://domain.tld/path/to/image-001.gif';
img2.src = 'http://domain.tld/path/to/image-002.gif';
img3.src = 'http://domain.tld/path/to/image-003.gif';
}
}
function addLoadEvent( func ) {
var oldonload = window.onload;
if ( typeof window.onload !== 'function' ) {
window.onload = func;
}
else {
window.onload = function () {
if ( oldonload ) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);

3、使用Ajax实现预加载

假设上面的所有方法都不够酷炫,那么,还有一种方法,就是使用Ajax来实现图片的预加载。这个方法是在Of Geeks and letters提出的,并且使用DOM来实现预加载,可以加载包括图片,CSS,JavaScript的其他任何东西。相对于直接使用JavaScript,使用Ajax的好处就是CSS和JavaScript可以在他们的内容不影响当前页面的情况下被预加载。对于图片来说这确实不是一个问题,尽管如此,这个方法依然很简洁高效:

1
2
3
4
5
6
7
8
9
10
11
12
13
window.onload = function () {
setTimeout(function () {
// XHR to request a JS and a CSS
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.js');
xhr.send('');
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.css');
xhr.send('');
// preload image
new Image().src = "http://domain.tld/preload.png";
}, 1000);
};

就像这样,这段代码会预加载三个文件:preload.jspreload.csspreload.png。设置1秒的延时主要是防止加载JavaScript文件而导致正常页面的功能性问题。

为了将其封装起来,我们看看怎么使用原生JavaScript来写这一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
window.onload = function() {
setTimeout(function() {
// reference to <head>
var head = document.getElementsByTagName('head')[0];
// a new CSS
var css = document.createElement('link');
css.type = 'text/css';
css.rel = 'stylesheet';
css.href = 'http://domain.tld/preload.css';
// a new JS
var js = document.createElement('script');
js.type = 'text/javascript';
js.src = 'http://domain.tld/preload.js';
// preload JS and CSS
head.appendChild(css);
head.appendChild(js);
// preload image
new Image().src = 'http://domain.tld/preload.png';
}, 1000);
};

在这里,我们通过DOM创建了三个元素来预加载了页面上的三个文件。正如原文中所提到的,对于Ajax来说,这个方法不是那么好。预加载的文件内容不应该添加到正在加载的页面中。

有更多的技巧吗?

我非常喜欢这些预加载的技巧,我只能想到这么多了。如果你有什么好的预加载的方法,包括对分享的这些技术的改进,用你对预加载更明智的建议来打动我的心吧。

相关链接