定位應(yīng)用的性能問題
Vue
應(yīng)用的性能問題可以分為兩個部分:運(yùn)行時性能問題暇韧,加載性能問題新博。
和其他 web
應(yīng)用一樣,定位 Vue
應(yīng)用性能問題最好的工具是 Chrome Devtool(F12 谷歌開發(fā)者工具)
,通過 Performance
工具可以用來錄制一段時間的 CPU
占用业簿、內(nèi)存占用、FPS
等運(yùn)行時性能問題阳懂,通過 Network
工具可以用來分析加載性能問題梅尤。
Chrome Performance
常見的名詞指標(biāo)
FP:First Paint
首次繪制柜思,第一幀數(shù)據(jù)渲染出來時
標(biāo)記瀏覽器渲染任何在視覺上不同于導(dǎo)航前的屏幕內(nèi)容的時間點(diǎn)FCP:First Contentful Paint
首次內(nèi)容繪制
標(biāo)記瀏覽器渲染來自DOM
第一位內(nèi)容的時間點(diǎn),該內(nèi)容可能是文本巷燥、圖像赡盘、SVG
,元素LCP:Largest Contentful Paint
最大內(nèi)容渲染缰揪,2019.11新增
代表在viewport
中最大的頁面元素加載的事件陨享。LCP
的數(shù)據(jù)會通過PerformanceEntry
對象記錄,每次出現(xiàn)更大的內(nèi)容渲染 則會產(chǎn)生一個新的PerformanceEntry
對象DCL:DomContentLoaded
當(dāng)HTML
文檔被完全加載和解析完成之后钝腺,DomContentLoaded
事件被觸發(fā)抛姑,無需等待樣式表、圖像和子框架的完成加載艳狐。FMP:First Meaningful Paint
首次有效繪制
主內(nèi)容的繪制定硝,視頻網(wǎng)站的主角元素自然是視頻,微博網(wǎng)站的博文是主要元素L:onLoad
當(dāng)依賴的資源全部加載完成后才會觸發(fā)TTI:Time to Interactive
可交互時間
用于標(biāo)記程序已進(jìn)行視覺渲染并能可靠響應(yīng)用戶輸入的時間點(diǎn)TBT:Total Blocking Time
頁面阻塞總時長
匯總所有加載過程中阻塞用戶操作的時長毫目,在FCP
和TTI
之間任何long task
(執(zhí)行時間超過一定閾值蔬啡,如50ms
)中阻塞部分都會被匯總FID:First Input Delay
首次輸入延遲
衡量從用戶首次與網(wǎng)站交互 到 瀏覽器實(shí)際能夠訪問 之間的時間SI:Speed Index
用于顯示頁面可見部分的顯示速度(時間)
我們通常要關(guān)心指標(biāo):FP、FCP镀虐、FMP
首頁白屏優(yōu)化
對于打包后的SPA
程序箱蟆,index.html
是一個空的,所以首次渲染的頁面也是空的(FP
)刮便,也就是白屏狀態(tài)空猜;
等待JS
加載完成,異步請求數(shù)據(jù)诺核,在未來的某一幀才開始渲染出大體內(nèi)容架構(gòu)(FCP
)抄肖,但仍缺少一些圖片等資源,且無法交互窖杀;
從而可知漓摩,為了減少白屏?xí)r間,可以把 FCP
階段提前:
- 骨架屏策略:在上線之前入客,通過
JS
或框架在index.html
中插入大體的頁面結(jié)構(gòu)管毙; - 預(yù)渲染:靜態(tài)渲染,對于不變的內(nèi)容在本地預(yù)渲染桌硫,而變化的內(nèi)容預(yù)留占位夭咬。
資源優(yōu)化
prefetch
prefetch
預(yù)獲取,分為三種類型:link prefetch铆隘、dns prefetch卓舵、prerender
-
link prefetch
在瀏覽器空閑時加載一個資源(HTML、JS膀钠、CSS掏湾、Image裹虫、Font
),是真正的資源下載融击;
注意:雖然預(yù)獲取了筑公,但頁面不會解析,<link ref="prefetch" > //谷歌 <link ref="prefetch" href="/uploads/images/bg.png">
JS
不會被執(zhí)行尊浪。 -
dns prefetch
前端優(yōu)化與DNS
相關(guān)的有兩點(diǎn):減少DNS
的請求次數(shù)匣屡,DNS
預(yù)解析
提前解析當(dāng)前頁面中與當(dāng)前域名不在同一個域的域名(第三方域名)拇涤,并緩存結(jié)果捣作,但不會下載任何資源,所以寫入具體的<!-- 開啟DNS預(yù)獲取,好像也不需要 --> <meta http-equiv="x-dns-prefetch-control" content="on"> <!-- 設(shè)置DNS預(yù)解析的域名 --> <link rel="dns-prefetch" />
JS工育、Image
等資源沒有意義虾宇。
當(dāng)用戶真正點(diǎn)擊網(wǎng)頁上的域名鏈接時,DNS
早已在后臺解析完成如绸,所以能減少等待時間,提升用戶體驗(yàn)旭贬。 -
prerender
預(yù)渲染
不僅會加載資源怔接,還會解析并預(yù)渲染頁面。但是否執(zhí)行預(yù)渲染是根據(jù)瀏覽器自身判斷的稀轨,瀏覽器可能會:- 分配少量資源對頁面進(jìn)行預(yù)渲染扼脐;
- 掛起部分請求直至頁面可見時;
- 可能放棄預(yù)渲染奋刽,如果消耗資源過多等等情況瓦侮。。佣谐。
很顯然肚吏,它是一個相對非常“重”的操作(資源浪費(fèi)嚴(yán)重)狭魂,瀏覽器會提前加載所有資源罚攀,并把渲染結(jié)果緩存在內(nèi)存中。<link rel="prerender" />
在SPA
項(xiàng)目中基本沒有應(yīng)用場景雌澄,對于MPA
項(xiàng)目斋泄,在確定用戶一定會進(jìn)入該頁面的情況下,可以考慮使用prerender
镐牺。
preconnect
preconnect
預(yù)連接
<!-- 谷歌使用了預(yù)鏈接 -->
<link ref="preconnect" >
瀏覽器要建立一個連接炫掐,需要經(jīng)過DNS
解析,TCP
三次握手和TLS
協(xié)商(https
)睬涧,這些過程也相當(dāng)?shù)暮臅r募胃。dns-prefetch
只是做了DNS
解析旗唁,而preconnect
把這三步都做了,使瀏覽器預(yù)先建立一個連接摔认,等真正需要加載資源時能直接請求逆皮。
preload
preload
預(yù)加載,它的作用是將資源率先加載参袱,聽起來容易和prefetch
混淆:
-
prefetch
是預(yù)獲取电谣,是對用戶接下來很可能會使用到的資源的預(yù)先下載; -
preload
本質(zhì)上是影響資源的加載順序抹蚀,把可能后置下載的資源前置下載剿牺。
preload
的特點(diǎn)
- 具有優(yōu)先級,但不會阻塞
onload
事件:preload
在網(wǎng)頁中具有強(qiáng)制加載的功能环壤,所以它的加載具有優(yōu)先級晒来,不過它僅僅是加載資源,并不會執(zhí)行郑现,所以仍需要script
加載資源; - 它設(shè)計(jì)的目的是為當(dāng)前頁面的資源進(jìn)行預(yù)加載湃崩,跳轉(zhuǎn)頁面后就使用不到了;
- 使用
as
字段來設(shè)定優(yōu)先級,as=style
則為最高優(yōu)先級接箫。優(yōu)先級順序?yàn)椋?code>HTML/CSS>Images>JS
舉個栗子
當(dāng)資源沒有直接體現(xiàn)在HTML
中攒读,而是隱藏在CSS
或JS
里,preload
可以提前告知瀏覽器隱藏資源的存在辛友,以便瀏覽器做出最優(yōu)的安排薄扁。
# style.css
@font-face {
font-family: myFirstFont;
src: url('https://fonts.gstatic.com/s/sofia/v8/8QIHdirahM3j_su5uI0Orbjl.woff2');
}
h1 {
font-family: myFirstFont;
}
- 未配置
preload
加載過程<head> <link rel="stylesheet" href="style.css"> </head>
對字體的下載發(fā)生在CSS
下載之后,因?yàn)橹挥挟?dāng)瀏覽器下載完CSS
并解析之后废累,才知道字體資源的存在邓梅;
- 配置上
preload
加載過程<link rel="preload" as="font" crossorigin="anonymous"> <link rel="stylesheet" href="style.css">
同時下載字體和CSS
,因?yàn)闉g覽器提前知道了隱藏資源的存在邑滨,做出了最優(yōu)安排日缨。與未配置preload
相比,下載時間減少了驼修。
小結(jié)
- 如果出現(xiàn)跨域無法加載殿遂,則加上
crossorigin
字段; - 這些優(yōu)化都是屬于勤快優(yōu)化類型乙各,將數(shù)據(jù)的加載和數(shù)據(jù)的使用/解析分開墨礁,且都有完整的工程化打包方案,如
webpack
耳峦,很多大站都會使用這些優(yōu)化方式恩静。
優(yōu)化無限列表性能
如果應(yīng)用中存在非常長或者無限滾動的列表,那么采用 窗口化 的技術(shù)來優(yōu)化性能,只需要渲染少部分區(qū)域的內(nèi)容驶乾,減少重新渲染組件和創(chuàng)建 DOM
節(jié)點(diǎn)的時間邑飒。
vue-virtual-scroll-list 和 vue-virtual-scroller 都是解決這類問題的開源項(xiàng)目。還可以參考 Google
工程師的文章Complexities of an Infinite Scroller
來嘗試自己實(shí)現(xiàn)一個虛擬的滾動列表來優(yōu)化性能级乐,主要使用到的技術(shù)是 DOM 回收疙咸、墓碑元素 和 滾動錨定。
組件懶加載
上面提到的無限列表的場景风科,比較適合列表內(nèi)元素非常相似的情況撒轮。不過有時候,你的Vue
應(yīng)用的超長列表中的內(nèi)容往往不盡相同贼穆,例如在一個復(fù)雜應(yīng)用的主界面中题山,由非常多不同的模塊組成,而用戶看到的往往只有首屏一兩個模塊故痊。但在初始渲染時顶瞳,不可見區(qū)域的模塊也會執(zhí)行和渲染,從而帶來一些額外的性能開銷愕秫。
使用組件懶加載在不可見時只需要渲染一個骨架屏慨菱,不需要真正渲染組件。
懶加載也叫延遲加載戴甩,即在需要的時候進(jìn)行加載抡柿,隨用隨載;
-
實(shí)現(xiàn)思路:
- 組件化
將各模塊拆分為組件粒度等恐,降低耦合度;將組件依賴的資源(比如請求接口备蚓、請求相關(guān)的依賴資源)全部封裝在組件內(nèi)部進(jìn)行調(diào)用课蔬; - 加載優(yōu)先級
優(yōu)先加載首屏可見模塊,其余不可見模塊懶加載郊尝,待可見或即將可見時加載二跋;
- 組件化
判斷可見性問題
通過監(jiān)聽scroll
、resize
事件來判斷模塊是否可見流昏,代碼不僅繁瑣扎即,而且一不小心沒有函數(shù)去抖就又可能導(dǎo)致嚴(yán)重的性能問題。
H5新增的IntersectionObserver API
是一個不錯的解決方案况凉,其設(shè)計(jì)是異步的谚鄙,回調(diào)是在主線程空閑時才執(zhí)行,而且保證回調(diào)執(zhí)行次數(shù)非常有限刁绒,在性能方面表現(xiàn)更優(yōu)闷营,使用起來也更簡單。
當(dāng)然,低版本瀏覽器還是要通過polyfill
兼容傻盟。盡可能懶的條件渲染
解決了可見性問題速蕊,懶加載就比較簡單了,Vue
提供的v-if
可以做到惰性渲染娘赴。-
如果可見后進(jìn)行初始渲染规哲,可見前如何顯示
如果在判斷加載條件為false
時,什么都不渲染诽表,就會帶來一系列問題:- 用戶體驗(yàn)比較差唉锌,最開始是白屏,然后突然又渲染出現(xiàn)內(nèi)容关顷;
- 最致命的是糊秆,判斷可見性需要一個目標(biāo)來觀察,如果什么不都渲染议双,那就無從觀察痘番。
因此,引入一個骨架屏的概念平痰,為真實(shí)的組件創(chuàng)建一個在尺寸汞舱、樣式上非常接近真實(shí)組件的組件。
骨架屏的作用:- 提升用戶感知體驗(yàn)宗雇;
- 保證切換的一致性昂芜;
- 提供可見性觀察的目標(biāo)對象。
如何提升切換時的體驗(yàn)
在真實(shí)組件開始渲染時赔蒲,需要一定的時間和空間泌神,時間指的是真實(shí)組件從創(chuàng)建到渲染的時間,包括請求接口舞虱、請求資源和渲染的時間欢际,空間指的是頁面布局中需要給真實(shí)組件留出剛好的位置,避免產(chǎn)生抖動矾兜。
我們可以使用Vue
內(nèi)置的transition
組件自定義骨架組件和真實(shí)組件的進(jìn)入和離開效果损趋,通過合理的布局和定位,減少切換時的抖動椅寺,通過設(shè)置過渡效果給真實(shí)組件留出一定的加載時間浑槽。Vue組件懶加載方案
--- Vue Lazy Component
該插件支持 組件可見或即將可見時懶加載,支持 組件延時加載返帕,支持 加載組件前展示組件骨架桐玻,提高用戶體驗(yàn),支持 懶加載組件分包異步加載溉旋。
API上的優(yōu)化
-
Object.freeze
Object.freeze()
:把不會修改的對象/數(shù)組冷凍起來畸冲,Vue
將不會對這些數(shù)據(jù)做響應(yīng)式處理。當(dāng)然,擅自修改這些數(shù)據(jù)也將會報錯給你看邑闲。const columnList = Object.freeze([ { title: '姓名', key: 'name', align: 'center' }, { title: '性別', key: 'gender', align: 'center' } ])