我在下面写的函数是否足以在大多数(如果不是全部的话)当前常用的浏览器中预加载图像?

function preloadImage(url)
{
    var img=new Image();
    img.src=url;
}

我有一个图像URL数组,我循环遍历并为每个URL调用preloadImage函数。


当前回答

这是最初的答案,但使用了更现代的ES语法:

let preloadedImages = [];
export function preloadImages(urls) {
    preloadedImages = urls.map(url => {
        let img = new Image();
        img.src = url;
        img.onload = () => console.log(`image url [${url}] has been loaded successfully`);
        
        return img;
    });
}

其他回答

const preloadImage = src => 
  new Promise((resolve, reject) => {
    const image = new Image()
    image.onload = resolve
    image.onerror = reject
    image.src = src
  })


// Preload an image
await preloadImage('https://picsum.photos/100/100')

// Preload a bunch of images in parallel 
await Promise.all(images.map(x => preloadImage(x.src)))

是的,这是可行的,但是浏览器会限制(4-8之间)实际的调用,因此不会缓存/预加载所有想要的图像。

更好的方法是在使用图像之前调用onload,如下所示:

function (imageUrls, index) {  
    var img = new Image();

    img.onload = function () {
        console.log('isCached: ' + isCached(imageUrls[index]));
        *DoSomething..*

    img.src = imageUrls[index]
}

function isCached(imgUrl) {
    var img = new Image();
    img.src = imgUrl;
    return img.complete || (img .width + img .height) > 0;
}

这就是我所做的,用承诺:


const listOfimages = [
    {
        title: "something",
        img: "https://www.somewhere.com/assets/images/someimage.jpeg"
    },  
    {
        title: "something else",
        img: "https://www.somewhere.com/assets/images/someotherimage.jpeg"
    }
];

const preload = async () => {
    await Promise.all(
        listOfimages.map(
            (a) =>
                new Promise((res) => {
                    const preloadImage = new Image();
                    preloadImage.onload = res;
                    preloadImage.src = a.img;
                })
        )
    );
}

以下是我的方法:

var preloadImages = function (srcs, imgs, callback) {
    var img;
    var remaining = srcs.length;
    for (var i = 0; i < srcs.length; i++) {
        img = new Image;
        img.onload = function () {
            --remaining;
            if (remaining <= 0) {
                callback();
            }
        };
        img.src = srcs[i];
        imgs.push(img);
    }
};

我可以确认,在这个问题的方法是足以触发图像下载和缓存(除非你已经禁止浏览器这样做通过你的响应头),至少:

铬74 Safari 12 火狐66 边缘17

为了测试这一点,我做了一个小的网络应用程序,它有几个端点,每个端点都休眠10秒钟,然后提供一张小猫的照片。然后我添加了两个网页,其中一个包含<script>标记,其中每个小猫都是使用来自问题的preloadImage函数预加载的,另一个包含页面上使用<img>标记的所有小猫。

在上面所有的浏览器中,我发现如果我先访问预加载器页面,等待一段时间,然后转到带有<img>标记的页面,我的小猫会立即呈现。这表明预加载器在所有测试的浏览器中成功地将小猫加载到缓存中。

您可以在https://github.com/ExplodingCabbage/preloadImage-test上查看或试用我用于测试的应用程序。

特别要注意的是,即使循环图像的数量超过浏览器一次愿意发出的并行请求的数量,这种技术也可以在上述浏览器中工作,这与Robin的回答所建议的相反。图像预加载的速率当然会受到浏览器愿意发送多少并行请求的限制,但它最终会请求您调用preloadImage()的每个图像URL。