前端資源預加載並展示進度條


  我們經常會看到,一些站點在首次進入的時候會先顯示一個進度條,等資源加載完畢后再呈現頁面,大概像這樣:

  

  然后整個頁面的操作就會非常流暢,因為之后沒必要再等待加載資源了。尤其是在移動端,或者是頁游中,這樣做能避免頁面出現白屏(等待加載圖片),很大程度提升用戶體驗。那這種技術是如何實現的呢?其實非常簡單,本文就來從基礎細節探究一番。

為什么需要資源預加載

     大多時候,我們的頁面並不是一次渲染完畢的,而是隨着用戶的操作,不斷修改DOM節點,如果你動態插入了一個圖片節點,那么瀏覽器要馬上發一個http請求,把圖片加載下來然后渲染在頁面上,如果用戶此時的網速不佳,那么加載這張圖片可能就會消耗幾秒鍾時間,此時頁面上什么都沒有(白屏)。最壞的情況,如果你的應用圖片很多,半天加載不出幾張圖,用戶很可能就在白屏的那幾秒跳走了。在游戲中更嚴重,主角的圖片如果加載不出來,讓用戶玩空氣去?

     除了在DOM中插入圖片節點,其他凡是涉及到要展示一張新圖片的操作,瀏覽器都得即時去請求圖片。比如,為某個節點添加如下css類來增加背景圖片:

.bg1{
     background: url(http://p2.qhimg.com/t01ed1438874f940dc0.jpg);
}

  或者是動態修改了src屬性、在canvas繪制圖片等,這些都會即時請求新資源。

     那么,資源預加載為什么能解決上述問題呢?

     我們預加載的資源,瀏覽器會緩存下來,再次使用的時候,瀏覽器會檢查是不是已經在緩存中,如果在,則直接用緩存的資源,不發送請求,或者由服務端返回304 not modified(304只帶請求頭信息,不傳輸資源)。這樣使用一張圖片的時間會大大縮減,我們的頁面看起來會非常流暢,媽媽再也不用擔心用戶會跳走了~

     也就是說,預加載的資源我們並不需要手動保存,由瀏覽器自動放到緩存了。

資源預加載的場景

     什么樣的項目需要預加載資源呢?

    范圍應該鎖定單頁面應用,SPA的視圖一般都是一步一步來呈現的,各種資源通過異步請求來獲取,為了追求原生app般的流暢體驗,可以把一些資源預加載下來。當然對於一些業務相關的圖片資源,也可考慮延遲加載,但延遲加載不是本文討論的范疇。

     視圖/圖片較多的專題頁面,或者是需要逐幀圖片來完成的動畫效果,最好都要做預加載。

     HTML5游戲,圖片一般都比較多,而且很多逐幀動畫,必須要預加載,事實上一些游戲引擎都會提供相應功能。

     哪些資源需要預加載呢?

     web中包含的資源有很多種,圖片、音視頻之類的媒體文件,另外就是js、css文件,這些都需要發送請求來獲取。那這些資源難道我們都預加載?

     當然不是了,預加載也是需要消耗時間的,總不能讓用戶等你加載個幾十秒鍾吧。具體預加載哪些資源,需要基於具體的考慮,也跟你的項目相關。以下是一些我的想法:

     js、css文件不需進行預加載。現在寫js基本都用requirejs之類的加載器,而且最后都會進行壓縮合並,將請求數降到最低,最終只有一個文件,有些團隊甚至還將壓縮后的代碼直接放在行內,這樣一個多余的請求都沒有了。

     那么需要預加載的就是媒體文件了,圖片、音視頻之類。這類資源也得根據實際情況來選擇哪些需要預加載。比如大多數頁面裝飾性圖片就需要預加載,而由業務動態獲取的圖片則無法預加載(預先不知道地址)。用作音效、小動畫的音視頻可以預加載,一個半小時長的視頻就不能預加載了。

預加載的原理與加載進度的獲取

     上面都是紙上談兵的一些觀點,下面我們該從技術的角度來思考一下預加載該如何實現。

     原理其實也相當簡單,就是維護一個資源列表,挨個去加載列表中的資源,然后在每個資源加載完成的回調函數中更新進度即可。

     以圖片為例,大致的代碼應該是這樣:

var image = new Image();
image.onload = function(){};
image.onerror = function(){};
image.src = url;

     這樣就OK啦,圖片已經進緩存,留着以后使用吧。

     再說進度,這個進度嚴格來講並不是文件加載的實時進度,因為我們只能在每個文件加載完成的時候執行回調,無法像timeline中那樣拿到文件加載的實時進度。

     計算方法就很簡單了,當前加載完的資源個數/資源總數*100,就是加載進度的百分比了。

資源預加載小插件:resLoader.js介紹

     本文的重點終於來了。。。額

     根據上面的原理,我寫了一個插件,用來做資源預加載。

     具備的特征如下:

         1. 自定義資源列表,用於預加載

         2. 自定義onProgress,想展示成進度條還是百分比數字還是個性的設計都可

         3. 開始和結束可配置回調函數

         4. 只支持圖片的預加載

         5. 支持amd、cmd加載器加載,同時支持直接用<script>標簽引入使用

         6. 不依賴其他庫

     用法如下:

var loader = new resLoader({
     resources : [
          'http://p2.qhimg.com/t01ed1438874f940dc0.jpg',
          'http://p9.qhimg.com/t01b4ff03b72c7dc6c7.jpg',
          'http://p2.qhimg.com/t01dd90dfbec92074d0.jpg',
          'http://p7.qhimg.com/t01cfec6d87cde457c5.jpg',
          'http://p9.qhimg.com/t01943ced462da67833.jpg',
          'http://p0.qhimg.com/t01943ced462da67833.jpg',
          'http://p6.qhimg.com/t01aa15a7ba7ccb49a7.jpg',
          'http://p8.qhimg.com/t010f1e8badf1134376.jpg',
          'http://p8.qhimg.com/t01cf37ea915533a032.jpg',
          'http://p3.qhimg.com/t0193d8a3963e1803e9.jpg',
          'http://p3.qhimg.com/t01cd6a4d4b4bd4457b.jpg'
     ],
     onStart : function(total){
          console.log('start:'+total);
     },
     onProgress : function(current, total){
          console.log(current+'/'+total);
          var percent = current/total*100;
          $('.progressbar').css('width', percent+'%');
          $('.progresstext .current').text(current);
          $('.progresstext .total').text(total);
     },
     onComplete : function(total){
          alert('加載完畢:'+total+'個資源');
     }
});

loader.start();

  各項參數都直接明了,不再多說了。在上面的例子中,我自己定義onProgress函數,做了一個簡單的進度條,你也可以做其他實現。函數為你傳入了current和total,分別表示當前完成的資源個數和資源總個數,可用於計算進度。

  效果可看在線demo:點擊這里

  另外附上下載地址,感興趣的朋友可以拿去使用:http://files.cnblogs.com/files/lvdabao/resLoader.zip

 

再多說兩句,關於xhr2新特性

     前邊的廢話貌似有點多。。。想直接用插件的下載下去用就好,有問題在此留言討論。

     這里想多說的東西是關於加載進度的,我上面說了我們只能獲取到的是進度其實是離散的點,不是連續的。其實利用HTML5的xhr2的新特性我們也可以嘗試獲取更加精確的進度。因為xhr2新增了一個非常有趣的特性:可以從服務端獲取文件數據。我們以前從服務端返回的數據都是字符串,現在可以直接返回Blob類型的文件。那么在這里做一個猜想,能不能利用此特性,做更加精確的進度計算呢?我在此處只是提出一種可能性,還未做實踐。我們知道xhr2新增的upload屬性可以讓我們獲取到文件上傳的進度信息,但對於返回的數據,卻無法直接提供進度信息,所以要想依靠它來實現還得做其他工作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM