1. 程式人生 > >淺談前端實現頁面載入進度條以及 nprogress.js 的實現

淺談前端實現頁面載入進度條以及 nprogress.js 的實現

以前在 Vue 的專案用了 nprogress 這個外掛,一直對於其如何得知載入進度充滿好奇,最近又看到了「前端如何實現頁面載入進度條」這個問題,今天週六恰好一探究竟。以下僅為一家之言,如有異議,歡迎指出。

前端的頁面載入進度條有兩種

首先不得不說,前端的頁面載入進度條其實有兩種,所以你得先搞清楚說的是哪一種。

第一種,進度條顯示的是 前端靜態資源 的載入。比如你開啟一個頁面,頁面需要載入 js、css、img 等靜態資源,那麼每載入完一個資源(監聽 onload 事件或者類似事件),進度條就向前滾動一下,直到載入完所有,進度條到頭。

實際操作中,如果不做前置靜態資源配置,基本不可能實現,因為你很難在程式碼中獲取頁面載入所需要的 js、css、img 資源,假設可以獲取,還需要監聽它們的 onload 事件,即使能實現這個進度條,也是一件 價效比很低

的事情,除非一個情況。

沒錯,這個特殊情況就是 遊戲資源的載入。我們在寫遊戲的時候,通常需要把靜態資源專案都列出來到配置中,而且,這個資源請求,一般比較耗時,這個時候,我們就需要這樣一個進度條,因為前置條件也已經滿足(資源已經列出),而如果只是寫一個普通的頁面,我們一般不會手動去列出靜態資源。(具體實現我沒有研究過,實際可能更加複雜,詳見 這個回答

第二種情況,也是我們現在通常說的進度條載入,舉個簡單的例子,GitHub 中就有用到。先開啟我的 GitHub 主頁 https://github.com/hanzichi,然後點選 tab 中的 Stars 標籤,這個時候 url 會變成 https://github.com/hanzichi?tab=stars

,進度條開始載入,當頁面內容切換過去的時候,進度條結束。

以上實現,其實就是 pjax 的實現,忽略掉 "p" 的部分,其實就是一個普通的 ajax。當頁面發起 ajax 請求的時候,顯示進度條,ajax 結束的時候,進度條到頭,從而實現整個頁面載入。這種情況,其實通常都會搭檔 SPA 出現。

NProgress

以上第二種情況,業界有個成熟的外掛 NProgress。它的 API 非常簡單,NProgress.start() 表示進度條開始,NProgress.done() 表示進度條結束。

如果搭配 pjax,可以這樣用:

$(document).on('pjax:start', function() { NProgress.start(); });
$(document).on('pjax:end',   function() { NProgress.done();  });

在 Vue 中,可以這樣用:

router.beforeEach((to, from, next) => {
  NProgress.start()
  next()
})

router.afterEach(() => {
  NProgress.done() // 結束 Progress
})

甚至,普通的頁面中也可以用,頁面開始的時候 NProgress.start(),window.onload 的回撥中執行 NProgress.done()

NProgress.start()NProgress.done() 過程中,進度條會不斷載入,時快時慢,這個速度的控制,依賴的是什麼?答案是,進度條的進度其實是假的,進度是 NProgress 自己在程式碼中控制的

我們可以看下 原始碼,呼叫 NProgress.start 後,會持續呼叫 NProgress.inc 方法,我們看下這個方法實現:

NProgress.inc = function(amount) {
  var n = NProgress.status;

  if (!n) {
    return NProgress.start();
  } else if(n > 1) {
    return;
  } else {
    if (typeof amount !== 'number') {
      if (n >= 0 && n < 0.2) { amount = 0.1; }
      else if (n >= 0.2 && n < 0.5) { amount = 0.04; }
      else if (n >= 0.5 && n < 0.8) { amount = 0.02; }
      else if (n >= 0.8 && n < 0.99) { amount = 0.005; }
      else { amount = 0; }
    }

    n = clamp(n + amount, 0, 0.994);
    return NProgress.set(n);
  }
};

程式碼中的 amount 就是進度條增量(0 為進度條起始值,1 為進度條終止值),可以從數值上判斷,進度條增長速度是越來越慢。當進度條增長到 99.4% 的時候,就停止了,直到呼叫 NProgress.done() 方法。

其實,某些場景,想獲取真實進度也是可以的,xhr2 其實是可以獲取進度的,用 ajax 上傳檔案就可以持續獲取進度進行展示,本文就不展開討論了。

本文的結論是,絕大多數情況下看到的前端頁面進度條展示,都是假的,只是特效 ...