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('載入時間');
複製程式碼
參考文章:
- 網站效能優化實戰——從12.67s到1.06s的故事
- Event Loop淺談
- JavaScript 工作原理之十一-渲染引擎及效能優化小技巧
- 基於Vue的SPA如何優化頁面載入速度
- 為vue專案新增骨架屏
- 瀏覽器允許的併發請求資源數是有限制的-分析
- 變態的靜態資源快取與更新 目前還只是個渣渣,以上只是我的個人理解,如有不足,還請指出,不勝感激。