背景是在某項目中的其中一個頁面,接入現場真實數據後,加載時間很長,長達10幾秒,嚴重影響用戶體驗。
在chrome
的DevTools
中,清緩存硬加載,可模擬用戶第一次訪問頁面的場景。根據Network
中的若干指標,對性能瓶頸初步判斷:前端
requests
:在HTTP / 1.0
或HTTP / 1.1
鏈接上,Chrome
每一個主機最多容許六個同時TCP
鏈接,因此請求數過多會致使TTFB
等待時間過長。xhr
:ajax
接口時間過長,瓶頸在於後端接口。Finish
:Finish
時間遠遠大於DOMContentLoaded
和Load
時間,說明頁面中的請求資源很大HTTP 2
的多路複用特性,合併請求;服務端渲染。ajax
接口時間過長,主要在後端接口的優化,Nodejs(BFF層)基於微服務的能力,因此要加快服務調用速度,減小服務調用數量,在業務上進行優化(緩存、批量接口、非必要數據異步請求...),固然自身代碼層的運行效率也要考慮。對比 PNG 原圖、PNG 無損壓縮、PNG 轉 WebP(無損)、PNG 轉 WebP(有損)的壓縮效果圖
loading
動畫、圖片的漸進式渲染(瀏覽器對一張圖片的加載順序基本上是下載了多少展現多少,讓用戶感受很刻板生硬,漸進式渲染就是圖片的內容從模糊到清晰的過程)、預加載(預見性的加載一些不可見區域的資源,提升用戶在快速滾動瀏覽器時候的體驗)。上述優化方向中,做者優化了Nodejs
層的api
接口時間,緩存了部分業務邏輯,剝離了部分底層接口使其異步獲取;同時選擇懶加載優化方向,對前端組件及圖片資源的加載進行優化。優化結果,肉眼可見。
懶加載並非一個新鮮的名詞,顧名思義,就是懶,如今不加載,稍後再加載,換個詞說就是,按需加載。由於不少場景下,暫時看不見用不到的資源是不須要同時加載的,浪費時間開支同時,也消耗了沒必要要的CPU
、IO
等資源。例如圖片懶加載在jQuery
時代就已經十分普及,在react
、vue
等前端MV*
框架的出現後,組件懶加載,SPA
單頁應用中的路由懶加載,webpack
中對初始化不須要加載的代碼塊進行懶加載,從而優化性能...如下做者重點介紹下圖片懶加載及vue
組件懶加載。vue
對於一些視頻圖片web應用,圖片懶加載幾乎是必需要作的,能夠大大提高用戶體驗。
img
標籤的src
設置縮略圖或者不設置src
,這裏的佔位圖能夠是缺省圖,loading圖;做者認爲這是懶加載最重要的環節
MDN中的定義:
Element.getBoundingClientRect()
方法返回元素的大小及其相對於視口的位置。
// 獲取元素的getBoundingClientRect屬性 const rect = Element.getBoundingClientRect(); if(rect.top < document.documentElement.clientHeight) { // 將top值與頁面的clientHeight進行對比,若小於則爲可視區域 ... }
PS:該方案須要監聽scroll
事件,注意節流處理。react
MDN中的定義:IntersectionObserver
接口 (從屬於Intersection Observer API
) 提供了一種異步觀察目標元素與其祖先元素或頂級文檔視窗(viewport
)交叉狀態的方法。Intersection Observer API 容許你配置一個回調函數,每當目標(target)元素與設備視窗或者其餘指定元素髮生交集的時候執行。設備視窗或者其餘元素咱們稱它爲根元素或根(root)。
var options = { root: document.querySelector('#scrollArea'), rootMargin: '0px', threshold: 1.0 // 目標(target)元素與根(root)元素之間的交叉度是交叉比(intersection ratio), 取值在0.0和1.0之間 } var observer = new IntersectionObserver(() => { // 回調函數,當目標元素和根元素交叉時觸發 ... }, options); var target = document.querySelector('#listItem'); // 添加目標元素,與根元素進行交叉狀態比對 observer.observe(target);
PS:該方案較前者的優勢就是不須要監聽,其實兼容性在chrome中還不錯。webpack
這裏的懶加載判斷依據和圖片相似,一樣是要判斷可視或者即將可視的時機,來控制組件的加載與否。當加載條件爲false
時,不作渲染,爲true
時則渲染,這裏用v-if
指令就能夠實現。git
在條件切換的同時,最好加入相似骨架屏的頁面,來過渡用戶體驗。github
社區裏這樣的方案有不少,評估後決定採用 vue-lazyload,star 5.7k,recent updates is 2 months ago,很穩。
npm i vue-lazyload -S // 在入口js中引入依賴,註冊在vue實例上 import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload, { lazyComponent: true });
這裏簡單提一下該方案的組件懶加載方案,在源碼L11-L16,利用render
來生成組件的內容this.$slots.default
,十分巧妙。web
render (h) { if (this.show === false) { return h(this.tag) } return h(this.tag, null, this.$slots.default) }
// 原代碼 <div class="camera-card-img" :style="{'backgroundImage': 'url(' + data._thumbnails + ')'}"> // 加入圖片懶加載邏輯 <div class="camera-card-img" v-lazy:background-image="data._thumbnails"> <lazy-component> // 須要懶加載的組件 ... </lazy-component>
52 requests, 19 imgajax
38 requests, 12 imgchrome
當瀏覽器繼續滾動的時候,圖片依次加載,能夠看到network中的request逐步增長至52,說明加載了剩餘圖片。npm
組件懶加載也是一樣的效果,數據量小可能頁面finish時間差感知不明顯,能夠加大模擬量至上千:
Finish
時間10s,
Finish
時間4s,速度提高顯著。
PS:這裏先後請求數不變的緣由,是做者在模擬數據的時候重複了若干次真實數據,致使資源地址都是重複的,瀏覽器會緩存請求,因此致使請求數不變。
其實能夠看到,數據量大的時候,加載速度依舊很慢,還須要繼續優化,能夠從如下幾個方向:
優化無止境,經常是花了大力氣,收效甚微。須要考慮時間和資源成本,優先解決投入產出比高的優化方向。以上是做者在實際項目中遇到的優化問題,僅供你們參考。