首屏優化系列(一)

下雨天DY發表於2018-06-29

title: 首屏優化系列(一) date: 2018-6-23 13:53:30 tags:

  • Promise
  • 圖片懶載入
  • 路由懶載入 categories: 前端

需求描述:優化首屏載入速度,減少白屏時間。

八一八本次專案中使用的優化手段,以及自己從中獲得的知識。 由於本次專案較為複雜,模組較多,(當然不比大型電商網站),首頁的請求就有8個,並且是精簡之後的,稍有不慎,白屏現象就會很嚴重。體驗極差。 ## 首頁模組劃分 1. 分類 2. 輪播圖 3. 推薦 4. 收藏數 5. 購買數 6. 案例展示 7. 主內容展示 8. 合作機構展示

其中位於首屏的是1、2、3、4、5,對於6在大螢幕上也會展示。

優化手段

Skeleton Screen (骨架屏)

  簡單來說,骨架屏就是在頁面內容未載入完成的時候,先使用一些圖形進行佔位,待內容載入完成之後再把它替換掉。體驗的效果就是,在頁面完全渲染完成之前,使用者會看到一個樣式簡單,描繪了當前頁面的大致框架,能夠感知到頁面正在逐步載入,最終骨架屏中各個佔位部分被完全替換。顯示效果如下:

首屏優化系列(一)
此處沒有單獨為整個頁面寫一個元件,而是為每個模組引入元件,如推薦課程這裡顯示5個課程,則在這部分資料的長度為0時,顯示5個佔位背景,當此模組資料請求完成,這部分就消失,顯示真實資料。這種手段只用在了課程首頁,因為只有這個頁面內請求較多。此外這種寫法也不易維護,很繁瑣。

路由懶載入

  把不同路由對應的元件分割為不同的程式碼塊,當路由被訪問的時候,再載入對應的元件,對中大型專案來說,會顯得很高效,對開發者而言,也方便維護。不過這裡要對生產環境和開發環境做區分,因為如果專案很大的話,每次更改程式碼觸發的熱更新時間都會很長,所以只在生產環境中使用路由懶載入。

// 生產環境  _import_production.js
module.exports = file => () => import('@/pages/' + file + '.vue');
// 開發環境 _import_development.js
module.exports = file => require('@/pages/' + file + '.vue').default; // vue-loader at least v13.0.0+
// router.js中引用
const _import = require('./_import_' + process.env.NODE_ENV);
{
    path: 'course',
    component: _import('course/index'),
    name: 'course'
}
複製程式碼

圖片懶載入(vue-lazyload)

// 安裝
npm install vue-lazyload --save-dev
// 使用 main.js
import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload)
Vue.use(VueLazyload, {
    preLoad: 1.8,
    error: require('@/assets/lazy/error.png'),
    loading: require('@/assets/lazy/loading.png'),
    attempt: 1,
    listenEvents: ['scroll']
});

new Vue({
  el: 'body',
  components: {
    App
  }
});
// 在使用圖片的地方加上v-lazy即可
<img v-lazy="img.src" >
複製程式碼

更多使用方式可參考vue-lazyload 這裡呈現的效果就是,請求完成資料,再去請求阿里雲存放的圖片,這個階段中,圖片資源載入過程中,顯示loading的狀態,載入完畢,顯示圖片,如果圖片載入失敗,顯示失敗的圖片。

Promise

  回到我們最初的問題,課程首頁的請求數為8,請求都為非同步請求,而我們的瀏覽器對同一域下的請求數量是有限制的,超過限制數目的請求會被阻塞,以谷歌瀏覽器的6個併發請求量為例,課程首頁的資料請求和圖片請求加起來成百上千個,上面,我們對圖片請求已經做了處理,使用懶載入的方式,另外,放在和資料請求不同的域下,我們需要考慮的就是資料請求,目前為8個,我做過這樣的測試,如果同時發出的話,每個請求的時間大概是50ms,當每次單獨傳送一個請求的時候,每個請求的請求時間大約十幾毫秒,所以我當時的解決方案就是使用setTimeOut,進行延遲請求,這樣做的話,我需要單獨測試每個請求的時間,然後為他們寫延遲時間,這樣效率是很低的。此外,跟promise比,setTimeOut的執行優先順序會降低,後來採用了promise的方式,如下:

return new Promise((resolve,reject) => {

})
複製程式碼

採用這種方式的另一個原因是,如果所有非同步請求同時觸發的話,瀏覽器會為他們分配執行的優先順序,而採用這種方式,請求的順序會按照我們呼叫的順序執行。而瀏覽器分配的話,可能頁面底部的請求會先執行。在請求較少的情況下,這種差異是體現的不明顯。

提到promise,我覺得最大的好處就是如它設計的初衷那樣,解決了層層回撥的問題(難以維護,且不優雅)--鏈式呼叫,還有Promise.all()的用法,對於組合資料很方便。

瀏覽器快取(localStorage)

對於不頻繁修改的資料做了快取,並且根據變換頻率,快取時間不同。

if (cacheTime < 0) cacheTime = 3600000;
var result = { data: response.data, expiration: new Date().getTime() + cacheTime };
localStorage.setItem(cacheName, JSON.stringify(result));
複製程式碼

詳細的快取方法,已上傳至github:前往檢視

PageSpeed Tools

  這個工具可以在谷歌的外掛商店中下載,很好用,能列出所有需要優化的點。 本地所提示的圖片問題,如使用瀏覽器快取,需要在伺服器設定etag和expire time 。   測試載入的時間可以用

console.time('載入時間');
console.timeEnd('載入時間');
複製程式碼

參考文章:

  1. 網站效能優化實戰——從12.67s到1.06s的故事
  2. Event Loop淺談
  3. JavaScript 工作原理之十一-渲染引擎及效能優化小技巧
  4. 基於Vue的SPA如何優化頁面載入速度
  5. 為vue專案新增骨架屏
  6. 瀏覽器允許的併發請求資源數是有限制的-分析
  7. 變態的靜態資源快取與更新 目前還只是個渣渣,以上只是我的個人理解,如有不足,還請指出,不勝感激。

相關文章