[Lift WebFramework] 利用 LazyLoad 來避免讀取時間過長與自訂讀取圖示

關於 LazyLoad 這東西

話說在 Web Application 裡面,有的時候有一些資訊的取得比較花時間,如果要等到處理完才顯示完整的網頁,那麼使用者可能會覺得很不耐煩,覺得你的網頁怎麼那麼慢。

舉例來講,我現在在做的一項東西裡面,有一個功能是和 GMail 的收件匣做連結,於是我希望能夠在偏好設定頁面裡面,顯示出是否能夠正常連上 GMail 的 IMAP 服務。

這個時候,與其等到確認完連線狀態後才把整個網頁吐給使用者,不如先顯示其他的網頁,然後放個「測試中……」的訊息,等到確認 IMAP 的狀態後再更新這個訊息。

當然,這個可以用 JavaScript / jQuery / AJAX 等等的方式做到,但 Lift 本身就提供了一個叫 LazyLoad 的 Snippet,我們可以直接使用 LazyLoad 這個 Snippet,這樣就不用自己去處理 JavaScript,也不用去管與伺服器交談和更新頁面上的資料等瑣碎的事項,只要專心處理資料取得的部份就好。

在沒有 LazyLoad 的場合

一開始,假設我們有一個取得 GMail 狀態的 Snippet 如下:

class GMailStatus extends JSImplicit {
    def render = {
        println("Start receive...")

        // 取得 GMail 狀態,這裡讓整個 Thraed 睡 10 秒來模擬與 IMAP 伺服
        // 器交談所花的時間
        Thread.sleep(10 * 1000)

        println("Done")

        // 把網頁模版裡 ID 是 gmailStatus 的標籤置換成「連線成功」的文字
        "#gmailStatus" #> "Connection OK"
    }
}

有了上面這個 Snippet 之後,在 Lift 裡面要使用上面的東西很簡單,我們只包一個 <div> 標籤,並給定 data-lift 屬性就好了。他會把被這個 <div> 包住的 HTML 丟給 GMailStatus 處理,而 GMailStatus 會依照 def render 裡的規則,把 <span id="gmailstatus"> 這個標籤給取代成「連線成功」的字樣。

<html>
  <head>
    <script src="/bootstrap/js/jquery.js"></script>
  </head>
  <body>
    GMail Status:
    <div data-lift="GMailStatus">
      <div id="gmailStatus">This text will be replaced.</div>
    </div>
  </body>
</html>

在這樣的場合下,當你連線到這一頁的時候,我們可以預期要等待十秒以上的時間,網站才會吐出整個網頁,而最後會吐出像下面的畫面:

GMailStatus 執行完後的狀態

GMailStatus 執行完後的狀態

加上 LazyLoad 來避免載入時間過長

當然,像這樣等到與伺服器交談完後才顯示整個網頁,會讓使用者覺得我們的網站很慢,這是我們所不樂見的狀況。

幸好 Lift Web Framework 提供了 LazyLoad 這個非常方便的東西,他一樣是個 Snippet,換句話說他的使用方法和上面我們看到的 GMailStatus 差不多,都是用一個 HTML 標籤把某個區塊包起來,而他的作用就是讓你這塊被包起來的區段先不顯示,等到資訊都 Ready 之後,才用 jQuery 來更新(所以你需要在模版中引入 jQuery 函式庫)。

整個使用方式看起來如下:

<html>
  <head>
    <script src="/bootstrap/js/jquery.js"></script>
  </head>
  <body>
    GMail Status:
    <div data-lift="LazyLoad">
      <div data-lift="GMailStatus2">
        <div id="gmailStatus">This text will be replaced.</div>
      </div>
    </div>
  </body>
</html>

沒錯,基本上我們原來的程式碼都不用去動他,只要在 HTML 模版裡呼叫到我們的 Snippet 的時候,在他外面包一層 LazyLoad 的 Snippet 呼叫就好了。

當我們加上 <div data-lift="LazyLoad"> 之後,該 <div> 下面的東西會變成 Lazy 的,一開始會顯示出 Loading 的訊息,而不是把整個網頁給 Block 住。

所以如果現在重新整理頁面的話,我們會馬上得到網頁的回應,但 LazyLoad 的那個 div 區塊會顯示 Loading 的字樣:

LazyLoad 讀取中

LazyLoad 讀取中

等到 GMailStatus 這個 Snippet 執行完後,就會變成 Connection OK 的訊息:

LazyLoad 完成

LazyLoad 完成

自訂讀取訊息模版

當然,那個 Loading 看起來有點醜,感覺上不是很適合用在實際的 production 網站上。

但也沒關係,LazyLoad 是允許你使用自訂的模版的。和 Snippet 的模版一樣,LazyLoad 的模版同樣也只是在 webapps/template-hidden/ 下的 HTML 檔案而已。

所以假設我們今天想要把 Loading 的字樣換成一個 Ajax Spinner 的圖示,那們只要建立一個 webapps/template-hidden/lazyLoading.html 的檔案,內容如下:

<img src="/img/loading.gif" alt="讀取中"/>

接著在呼叫 LazyLoad 的時候,給定 template 參數即可:

<html>
  <head>
    <script src="/bootstrap/js/jquery.js"></script>
  </head>
  <body>
    GMail Status:
    <div data-lift="LazyLoad?template=lazyLoading">
      <div data-lift="GMailStatus">
        <div id="gmailStatus">This text will be replaced.</div>
      </div>
    </div>
  </body>
</html>

加上了 template 參數之後,重新讀取網頁的話,就可以看到現在 LazyLoad 是使用我們的 Ajax Spinner 來顯示囉:

使用自訂 LazyLoad 模版

使用自訂 LazyLoad 模版

結論

  • 當你的 Snippet 需要執行較長時間時,可以使用 LazyLoad 來避免使用者等待
  • 要使用 LazyLoad 的話,你需要 include jQuery 函式庫到你的網頁中
  • 你可以使用 template 參數來客制化 LazyLoad 讀取時出現的訊息

回響