核心要点
- 使用Promise异步加载图片,允许同时加载不同图片集合,并在集合加载完成后执行代码。这通过减少整体加载时间来显着提高网站性能。
- 此技术涉及为所有图片“组”(集合)创建一个共享预加载器,该预加载器将要加载的图片排队。然后,预加载器并行(而非顺序)开始加载图片,避免必须等待一个组完成才能开始下一个组。
- 每个图片URL都替换为一个Promise,该Promise在浏览器加载图片后解析。然后,可以使用
Promise.all()
方法为每个组创建一个Promise,该Promise在数组中的所有Promise都解析后解析。 - 通过使用延迟Promise而不是回调来告诉预加载器在组加载完成后该做什么,可以进一步改进该技术。这允许稍后控制Promise的解析。
本文探讨一个具体问题:如何并行预加载大量图片。 我最近遇到了这个问题,发现它比最初预期的更具挑战性,也从中学习了很多。首先,让我简要描述一下场景。假设页面上有几个“组”。广义上说,一个组就是一个图片集合。我们希望预加载每个组的图片,并能够知道何时完成某个组的图片加载。此时,我们可以自由运行任何我们想要的代码,例如向组添加一个类、运行图像序列、记录某些内容等等。起初,这听起来很简单,甚至非常简单。但是,你可能和我一样忽略了一个细节:我们希望所有组并行加载,而不是顺序加载。换句话说,我们不希望先加载组1的所有图片,然后加载组2的所有图片,再加载组3的所有图片,依此类推。事实上,这不是理想的,因为最终会有一些组需要等待前面的组完成。因此,在一个场景中,如果第一个组有几十张图片,而第二个组只有一两张图片,我们就必须等待第一个组完全加载才能准备第二个组。这不好。我们肯定可以做得更好!所以我们的想法是并行加载所有组,这样当一个组完全加载时,我们不必等待其他组。为此,大致思路是加载所有组的第一张图片,然后加载所有组的第二张图片,依此类推,直到所有图片都已预加载。好了,让我们从创建一些标记开始,这样我们就能就正在发生的事情达成一致。
顺便说一句,在本文中,我假设您熟悉Promise的概念。如果不是这样,我建议您阅读这篇文章。
标记
从标记的角度来看,一个组只不过是一个元素(例如div),带有deck类以便我们可以定位它,以及一个包含图片URL数组(作为JSON)的data-images属性。
<div class="deck" data-images='["...", "...", "..."]'>...</div> <div class="deck" data-images='["...", "..."]'>...</div> <div class="deck" data-images='["...", "...", "...", "..."]'>...</div>
准备工作
在JavaScript方面,这——不出所料——有点复杂。我们将构建两样不同的东西:一个组类(请将此放在非常大的引号之间,不要对术语吹毛求疵)和一个预加载器工具。因为预加载器必须知道所有组的所有图片才能以特定的顺序加载它们,所以它需要在所有组之间共享。一个组不能有它自己的预加载器,否则我们会遇到最初的问题:代码是顺序执行的,这不是我们想要的。所以我们需要一个传递给每个组的预加载器。后者将它的图片添加到预加载器的队列中,一旦所有组都将它们的项目添加到队列中,预加载器就可以开始预加载。执行代码片段如下:
// 实例化一个预加载器 var ip = new ImagePreloader(); // 从DOM获取所有组 var decks = document.querySelectorAll('.deck'); // 遍历它们并为每个组实例化一个新的组,将预加载器传递给每个组,以便组可以将它的图片添加到队列中 Array.prototype.slice.call(decks).forEach(function (deck) { new Deck(deck, ip); }); // 一旦所有组都将它们的项目添加到队列中,就预加载所有内容 ip.preload();
我希望到目前为止,这是有意义的!
构建组
根据您想对组做什么,这个“类”可能相当长。对于我们的场景,我们唯一要做的事情是在其图片加载完成后向节点添加一个loaded类。Deck函数没有太多工作要做:1. 加载数据(从data-images属性);2. 将数据添加到预加载器队列的末尾;3. 告诉预加载器在数据预加载完成后该做什么。
var Deck = function (node, preloader) { // 我们从`data-images`属性获取并解析数据 var data = JSON.parse(node.getAttribute('data-images')); // 我们调用预加载器的`queue`方法,将数据和回调函数传递给它 preloader.queue(data, function () { node.classList.add('loaded'); }); };
到目前为止,进展顺利,不是吗?唯一剩下的就是预加载器,尽管它也是本文中最复杂的代码部分。
构建预加载器
我们已经知道我们的预加载器需要一个queue方法来将图片集合添加到队列中,以及一个preload方法来启动预加载。它还需要一个辅助函数来预加载图片,称为preloadImage。让我们从这里开始:
var ImagePreloader = function () { ... }; ImagePreloader.prototype.queue = function () { ... } ImagePreloader.prototype.preloadImage = function () { ... } ImagePreloader.prototype.preload = function () { ... }
预加载器需要一个内部queue属性来保存它必须预加载的组,以及它们各自的回调。
var ImagePreloader = function () { this.items = []; }
items是一个对象数组,其中每个对象有两个键:- collection包含要预加载的图片URL数组;- callback包含在组完全加载后要执行的函数。
知道了这一点,我们可以编写queue方法。
<div class="deck" data-images='["...", "...", "..."]'>...</div> <div class="deck" data-images='["...", "..."]'>...</div> <div class="deck" data-images='["...", "...", "...", "..."]'>...</div>
好了。此时,每个组都可以将它的图片添加到队列中。我们现在必须构建preload方法,它将负责实际预加载图片。但在跳转到代码之前,让我们退一步来理解我们需要做什么。我们的想法不是一个接一个地预加载每个组的所有图片。我们的想法是预加载每个组的第一张图片,然后是第二张,然后是第三张,依此类推。预加载一张图片意味着使用JavaScript(使用new Image())创建一个新的图片,并为其应用一个src。这将提示浏览器异步加载源。由于这个异步过程,我们需要注册一个Promise,该Promise在浏览器下载资源后解析。基本上,我们将用一个Promise替换我们数组中的每个图片URL,该Promise在浏览器加载给定图片后解析。此时,我们将能够使用Promise.all(..) 来获得一个最终的Promise,该Promise在数组中的所有Promise都解析后解析。对于每个组都是如此。让我们从preloadImage方法开始:
// 实例化一个预加载器 var ip = new ImagePreloader(); // 从DOM获取所有组 var decks = document.querySelectorAll('.deck'); // 遍历它们并为每个组实例化一个新的组,将预加载器传递给每个组,以便组可以将它的图片添加到队列中 Array.prototype.slice.call(decks).forEach(function (deck) { new Deck(deck, ip); }); // 一旦所有组都将它们的项目添加到队列中,就预加载所有内容 ip.preload();
现在是preload方法。它做两件事(因此可能可以拆分成两个不同的函数,但这不在本文的范围内):1. 它以特定的顺序(每个组的第一张图片,然后是第二张,然后是第三张……)将所有图片URL替换为Promise;2. 对于每个组,它注册一个Promise,当组中的所有Promise都解析后(!)调用组的回调。
var Deck = function (node, preloader) { // 我们从`data-images`属性获取并解析数据 var data = JSON.parse(node.getAttribute('data-images')); // 我们调用预加载器的`queue`方法,将数据和回调函数传递给它 preloader.queue(data, function () { node.classList.add('loaded'); }); };
就是这样!毕竟没有那么复杂,你同意吗?
进一步推进
代码运行良好,尽管使用回调来告诉预加载器在组加载完成后该做什么并不是很优雅。您可能希望使用Promise而不是回调,尤其是在我们一直使用Promise的情况下!我不确定如何解决这个问题,所以我不得不承认我请我的朋友Valérian Galliat帮我解决这个问题。我们在这里使用的是延迟Promise。延迟Promise不是原生Promise API的一部分,因此我们需要为其添加polyfill;谢天谢地,这只需要几行代码。基本上,延迟Promise是一个稍后可以解析的Promise。将其应用于我们的代码,只会改变很少的东西。首先是.queue(..)
方法:
var ImagePreloader = function () { ... }; ImagePreloader.prototype.queue = function () { ... } ImagePreloader.prototype.preloadImage = function () { ... } ImagePreloader.prototype.preload = function () { ... }
.preload(..)
方法中的解析:
var ImagePreloader = function () { this.items = []; }
当然,最后是我们添加数据到队列的方式!
// 如果没有指定回调,则为空函数 function noop() {} ImagePreloader.prototype.queue = function (array, callback) { this.items.push({ collection: array, // 如果没有回调,我们推送一个no-op(空)函数 callback: callback || noop }); };
我们完成了!如果您想查看代码的实际运行情况,请查看下面的演示:(此处应插入CodePen演示链接,因为我无法直接嵌入CodePen)
结论
好了,朋友们。大约70行JavaScript代码,我们就成功地异步并行加载了不同集合中的图片,并在集合加载完成后执行了一些代码。从这里开始,我们可以做很多事情。在我的例子中,重点是在点击按钮时将这些图片作为快速循环序列(gif样式)运行。因此,我在加载期间禁用了按钮,并在组完成所有图片的预加载后重新启用它。由于浏览器已经缓存了所有图片,因此第一个循环运行非常流畅。我希望你喜欢它!您可以在GitHub上查看代码,也可以直接在CodePen上使用它。(此处应插入GitHub链接和CodePen链接)
(此处应添加FAQ部分,与输入文本中的FAQ部分内容一致,但语言表达上进行了一些调整和润色。)
以上是与承诺并行预加载图像的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

本教程向您展示了如何将自定义的Google搜索API集成到您的博客或网站中,提供了比标准WordPress主题搜索功能更精致的搜索体验。 令人惊讶的是简单!您将能够将搜索限制为Y

利用轻松的网页布局:8个基本插件 jQuery大大简化了网页布局。 本文重点介绍了简化该过程的八个功能强大的JQuery插件,对于手动网站创建特别有用

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

核心要点 JavaScript 中的 this 通常指代“拥有”该方法的对象,但具体取决于函数的调用方式。 没有当前对象时,this 指代全局对象。在 Web 浏览器中,它由 window 表示。 调用函数时,this 保持全局对象;但调用对象构造函数或其任何方法时,this 指代对象的实例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。这些方法使用给定的 this 值和参数调用函数。 JavaScript 是一门优秀的编程语言。几年前,这句话可

jQuery是一个很棒的JavaScript框架。但是,与任何图书馆一样,有时有必要在引擎盖下发现发生了什么。也许是因为您正在追踪一个错误,或者只是对jQuery如何实现特定UI感到好奇

该帖子编写了有用的作弊表,参考指南,快速食谱以及用于Android,BlackBerry和iPhone应用程序开发的代码片段。 没有开发人员应该没有他们! 触摸手势参考指南(PDF) Desig的宝贵资源


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

Dreamweaver Mac版
视觉化网页开发工具

Atom编辑器mac版下载
最流行的的开源编辑器

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),