與承諾並行預加載圖像
核心要點
- 使用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中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。
