<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          vue項(xiàng)目你一定會(huì)用到的性能優(yōu)化!

          共 6751字,需瀏覽 14分鐘

           ·

          2022-05-15 19:28

          大廠技術(shù)??高級(jí)前端??Node進(jìn)階

          點(diǎn)擊上方?程序員成長(zhǎng)指北,關(guān)注公眾號(hào)

          回復(fù)1,加入高級(jí)Node交流群

          提起性能優(yōu)化 很多人眼前浮現(xiàn)的面試經(jīng)驗(yàn)是不是歷歷在目呢?反正,性能優(yōu)化在我看來(lái)他永遠(yuǎn)是前端領(lǐng)域的熱度之王。

          而本渣最近維護(hù)的項(xiàng)目恰巧在這個(gè)方向下了很大功夫,一些經(jīng)驗(yàn)之談奉上,希望對(duì)大家有些許幫助!

          性能優(yōu)化標(biāo)準(zhǔn)

          既然說(shuō)性能優(yōu)化,那他總得有一個(gè)公認(rèn)的標(biāo)準(zhǔn),這就是我們很多次聽(tīng)到的Lighthouse

          image.png

          在很多單位,都有著自己的性能監(jiān)控平臺(tái),我們只需要引入相應(yīng)的sdk,那么在平臺(tái)上就能分析出你頁(yè)面的存在的性能問(wèn)題,大家是不是學(xué)的很神奇!

          其實(shí)除了苛刻的業(yè)務(wù),需要特殊的定制,大多數(shù)的情況下我們單位的性能優(yōu)化平臺(tái)本質(zhì)上其實(shí)就是利用無(wú)頭瀏覽器(Puppeteer)跑Lighthouse。

          理解了我們單位的性能監(jiān)控平臺(tái)的原理之后,我們就能針對(duì)性的做性能優(yōu)化,也就是面向Lighthouse編程

          Lighthouse

          lighthouse[1]?是 Google Chrome 推出的一款開(kāi)源自動(dòng)化工具,它可以搜集多個(gè)現(xiàn)代網(wǎng)頁(yè)性能指標(biāo),分析 Web 應(yīng)用的性能并生成報(bào)告,為開(kāi)發(fā)人員進(jìn)行性能優(yōu)化的提供了參考方向。

          說(shuō)起Lighthouse在現(xiàn)代的谷歌瀏覽器中業(yè)已經(jīng)集成

          image.png

          他可以分析出我們的頁(yè)面性能,通過(guò)幾個(gè)指標(biāo)

          Lighthouse 會(huì)衡量以下性能指標(biāo)項(xiàng):

          • 首次內(nèi)容繪制[2](First Contentful Paint)。即瀏覽器首次將任意內(nèi)容(如文字、圖像、canvas 等)繪制到屏幕上的時(shí)間點(diǎn)。

          • 可交互時(shí)間[3](Time to Interactive)。指的是所有的頁(yè)面內(nèi)容都已經(jīng)成功加載,且能夠快速地對(duì)用戶的操作做出反應(yīng)的時(shí)間點(diǎn)。

          • 速度指標(biāo)[4](Speed Index)。衡量了首屏可見(jiàn)內(nèi)容繪制在屏幕上的速度。在首次加載頁(yè)面的過(guò)程中盡量展現(xiàn)更多的內(nèi)容,往往能給用戶帶來(lái)更好的體驗(yàn),所以速度指標(biāo)的值約小越好。

          • 總阻塞時(shí)間[5](Total Blocking Time)。指First Contentful Paint 首次內(nèi)容繪制 (FCP)與Time to Interactive 可交互時(shí)間 (TTI)之間的總時(shí)間

          • 最大內(nèi)容繪制[6](Largest Contentful Paint)。度量標(biāo)準(zhǔn)報(bào)告視口內(nèi)可見(jiàn)的最大圖像或文本塊的呈現(xiàn)時(shí)間

          • 累積布局偏移[7](# Cumulative Layout Shift)。衡量的是頁(yè)面整個(gè)生命周期中每次元素發(fā)生的非預(yù)期布局偏移得分的總和。每次可視元素在兩次渲染幀中的起始位置不同時(shí),就說(shuō)是發(fā)生了LS(Layout Shift)。

          在一般情況下,據(jù)我的經(jīng)驗(yàn),由于性能監(jiān)控平臺(tái)的和本地平臺(tái)的差異,本地可能要達(dá)到70分,線上才有可能達(dá)到及格的狀態(tài),如果有性能優(yōu)化的需求時(shí),大家酌情處理即可(不過(guò)本人覺(jué)得,及格即可, 畢竟大學(xué)考試有曰:60分萬(wàn)歲,61分浪費(fèi),傳承不能丟,咱們要把更多的時(shí)間,放到更重要的事情上來(lái)!)

          通用常規(guī)優(yōu)化手段

          lighthouse的的牛x之處就是它能找出你頁(yè)面中的一些常規(guī)的性能瓶頸,并提出優(yōu)化建議,比如:

          image.png
          image.png

          于是針對(duì)這些優(yōu)化建議,我們需要做一些常規(guī)的優(yōu)化:

          1. 減少未使用的javascript
          2. 移出阻塞渲染的資源
          3. 圖片質(zhì)量壓縮
          4. 限制使用字體數(shù)量,盡可能少使用變體
          5. 優(yōu)化關(guān)鍵渲染路徑:只加載當(dāng)前頁(yè)面渲染所需的必要資源,將次要資源放在頁(yè)面渲染完成后加載

          通用性能優(yōu)化分析

          我們知道lighthouse 中有六個(gè)性能指標(biāo),而在這六個(gè)指標(biāo)中,LCP、 FCP、speed index、 這三個(gè)指數(shù)尤為重要,因?yàn)樵谝话闱闆r下 這個(gè)三個(gè)指標(biāo)會(huì)影響 TTI、TBT、CLS 的分?jǐn)?shù)

          所以在我們?cè)趦?yōu)化時(shí), 需要提高LCP、 FCP和speedIndex 的分?jǐn)?shù),經(jīng)過(guò)測(cè)試, 即使是空頁(yè)面也會(huì)有時(shí)間上的損耗, 初始分?jǐn)?shù)基本都是0.8

          注意:需要值得大家注意的是,我們當(dāng)前所有測(cè)試全部建立在,移動(dòng)端(之所以用移動(dòng)端,是由于pc 的強(qiáng)大算力,很少有性能瓶頸)的基礎(chǔ)上,并且頁(yè)面上必須有一下內(nèi)容,才能得出分?jǐn)?shù),內(nèi)容必須包括一下的一種或者多種

          • 內(nèi)嵌在svg元素內(nèi)的image元素
          • video元素(使用封面圖像)
          • 通過(guò)url\(\)[8]函數(shù)(而非使用CSS 漸變[9])加載的帶有背景圖像的元素
          • 包含文本節(jié)點(diǎn)或其他行內(nèi)級(jí)文本元素子元素的塊級(jí)元素[10]

          否則就會(huì)有如下錯(cuò)誤

          image.png

          接下來(lái)我們就從LCP、 FCP和speedIndex 這三個(gè)指標(biāo)入手

          FCP(First Contentful Paint)

          顧名思義就是首次內(nèi)容繪制,也就是頁(yè)面最開(kāi)始繪制內(nèi)容的時(shí)間,但是由于我們現(xiàn)在開(kāi)發(fā)的頁(yè)面都是spa應(yīng)用,所以,框架層面的初始化是一定會(huì)有一定的性能損耗的,以vue-cli 搭建的腳手架為例,當(dāng)我初始化空的腳手架,打包后上傳cdn部署,F(xiàn)CP 就會(huì)從0.8s提上到1.5秒,由此可見(jiàn)vue 的diff 也不是免費(fèi)的他也會(huì)有性能上的損耗

          在優(yōu)化頁(yè)面的內(nèi)容之前我們聲明三個(gè)前提

          1. 提高FCP的時(shí)間其實(shí)就是在優(yōu)化關(guān)鍵渲染路徑[11]
          2. 如果它是一個(gè)樣式文件(CSS文件),瀏覽器就必須在渲染頁(yè)面之前完全解析它(這就是為什么說(shuō)CSS具有渲染阻礙性)
          3. 如果它是一個(gè)腳本文件(JavaScript文件),瀏覽器必須:停止解析,下載腳本,并運(yùn)行它。只有在這之后,它才能繼續(xù)解析,因?yàn)?JavaScript 腳本可以改變頁(yè)面內(nèi)容(特別是HTML)。(這就是為什么說(shuō)JavaScript阻塞解析)

          針對(duì)以上的用例測(cè)試,我們發(fā)現(xiàn),無(wú)論我們?cè)趺磧?yōu)化,框架本身的性能損耗是無(wú)法抹除的,我們唯一能做的就是讓框架更早的去執(zhí)行初始化,并且初始化更少的內(nèi)容,可做的優(yōu)化手段如下:

          1. 所有初始化用不到的js 文件全部走異步加載,也就是加上defer或者asnyc ,并且一些需要走cdn的第三方插件需要放在頁(yè)面底部(因?yàn)榉旁陧敳?,他的解析?huì)阻止html 的解析,從而影響css 等文件的下載,這也是雅虎軍規(guī)的一條)
          2. js 文件拆包,以vue-cli 為例,一般情況下我們可以通過(guò)cli的配置 splitChunks 做代碼分割,將一些第三方的包走cdn,或者拆包。如果有路由的情況下將路由做拆包處理,保證每個(gè)路由只加載當(dāng)前路由對(duì)應(yīng)的js代碼
          3. 優(yōu)化文件大小 減少字體包、css文件、以及js文件的大小(當(dāng)然這些 腳手架默認(rèn)都已經(jīng)做了)
          4. 優(yōu)化項(xiàng)目結(jié)構(gòu),每個(gè)組件的初始化都是有性能損耗的,在在保證可維護(hù)性的基礎(chǔ)上,盡量減少初始化組件的加載數(shù)量

          5、網(wǎng)絡(luò)協(xié)議層面的優(yōu)化,這個(gè)優(yōu)化手段需要服務(wù)端配合純前端已經(jīng)無(wú)法達(dá)到,在現(xiàn)在云服務(wù)器盛行的時(shí)代,自家單位一般都會(huì)默認(rèn)在云服務(wù)器中開(kāi)啟這些優(yōu)化手段,比如開(kāi)啟gzip,使用cdn 等等

          其實(shí)說(shuō)來(lái)說(shuō)去,提高FCP 的核心只有理念之后兩個(gè) 減少初始化視圖內(nèi)容減少初始化下載資源大小

          LCP(Largest Contentful Paint)

          顧名思義就是最大內(nèi)容繪制, 何時(shí)報(bào)告LCP,官方是這樣說(shuō)的

          為了應(yīng)對(duì)這種潛在的變化,瀏覽器會(huì)在繪制第一幀后立即分發(fā)一個(gè)largest-contentful-paint類型的`PerformanceEntry`[12],用于識(shí)別最大內(nèi)容元素。但是,在渲染后續(xù)幀之后,瀏覽器會(huì)在最大內(nèi)容元素發(fā)生變化時(shí)分發(fā)另一個(gè)`PerformanceEntry`[13]。

          例如,在一個(gè)帶有文本和首圖的網(wǎng)頁(yè)上,瀏覽器最初可能只渲染文本部分,并在此期間分發(fā)一個(gè)largest-contentful-paint條目,其element屬性通常會(huì)引用一個(gè)

          ?。隨后,一旦首圖完成加載,瀏覽器就會(huì)分發(fā)第二個(gè)largest-contentful-paint條目,其element屬性將引用?。

          需要注意的是,一個(gè)元素只有在渲染完成并且對(duì)用戶可見(jiàn)后才能被視為最大內(nèi)容元素。尚未加載的圖像不會(huì)被視為"渲染完成"。在字體阻塞期[14]使用網(wǎng)頁(yè)字體的文本節(jié)點(diǎn)亦是如此。在這種情況下,較小的元素可能會(huì)被報(bào)告為最大內(nèi)容元素,但一旦更大的元素完成渲染,就會(huì)通過(guò)另一個(gè)PerformanceEntry對(duì)象進(jìn)行報(bào)告。

          其實(shí)用大白話解釋就是,通常情況下,圖片、視頻以及大量文本繪制完成后就會(huì)報(bào)告LCP

          理解了這一點(diǎn),的優(yōu)化手段就明確了,盡量減少這些資源的大小就可以了,經(jīng)過(guò)測(cè)試,減少首屏渲染的圖片以及視頻內(nèi)容大小后,整體分?jǐn)?shù)顯著提高,提供一些優(yōu)化方法:

          1. 本地圖片可以使用在線壓縮工具自己壓縮 推薦tinypng.com[15]
          2. 接口中附帶圖片,一般情況下單位中都有對(duì)應(yīng)的oss或者cdn傳參配置通過(guò)地址欄傳參方式控制圖片質(zhì)量
          3. 圖片懶加載

          SpeedIndex(速度指數(shù))

          Speed Index采用可視頁(yè)面加載的視覺(jué)進(jìn)度,計(jì)算內(nèi)容繪制速度的總分。為此,首先需要能夠計(jì)算在頁(yè)面加載期間,各個(gè)時(shí)間點(diǎn)“完成”了多少部分。在WebPagetest中,通過(guò)捕獲在瀏覽器中加載頁(yè)面的視頻并檢查每個(gè)視頻幀(在啟用視頻捕獲的測(cè)試中,每秒10幀)來(lái)完成的,這個(gè)算法在下面有描述,但現(xiàn)在假設(shè)我們可以為每個(gè)視頻幀分配一個(gè)完整的百分比(在每個(gè)幀下顯示的數(shù)字)

          以上是官方解釋的計(jì)算方式,其實(shí)通俗的將,所謂速度指數(shù)就是衡量頁(yè)面內(nèi)容填充的速度

          image.png

          一圖勝千言

          經(jīng)過(guò)測(cè)試,跟LCP相同,圖片以及視頻內(nèi)容對(duì)于SpeedIndex的影響巨大,所有優(yōu)化方向,通之前一致,總的來(lái)說(shuō),只要提高LCP 以及FCP 的時(shí)間SpeedIndex 的時(shí)間就會(huì)有顯著提高

          不過(guò)需要注意的是,接口的速度也會(huì)影響SpeedIndex的時(shí)間,由于AJAX流行的今天,我們大多數(shù)的數(shù)據(jù)都是使用接口拉取。如果接口速度過(guò)慢,他就會(huì)影響你頁(yè)面的初始渲染, 導(dǎo)致性能問(wèn)題,所以,在做性能優(yōu)化的同時(shí),請(qǐng)求后端伙伴協(xié)助,也是性能優(yōu)化的一個(gè)方案

          排查性能瓶頸

          上述分析,根據(jù)三個(gè)指標(biāo)提供了一些常規(guī)的優(yōu)化手段,那么在這些優(yōu)化手段中,有的你可以立馬排查到,并且優(yōu)化例如:

          1. 優(yōu)化圖像,優(yōu)化字體大小
          2. 跟服務(wù)端配合利用瀏覽器緩存機(jī)制.啟用cdn、啟用gzip等
          3. 減少網(wǎng)絡(luò)協(xié)議過(guò)程中的消耗,減少http 請(qǐng)求、減少dns查詢、避免重定向
          4. 優(yōu)化關(guān)鍵渲染路徑,異步加載js等

          但是有的優(yōu)化手段我們不容易排查,因?yàn)樗谴蛟诎锩娴?,這個(gè)js 文件包含了很多邏輯怎么辦,這里我有兩個(gè)手段或許能夠幫助排查出性能瓶頸發(fā)生在哪里:

          分析包內(nèi)容

          在通常情況下,我們無(wú)法判斷的優(yōu)化點(diǎn),都是在打包后,我們無(wú)法分析出,那些東西不是我們?cè)谑灼帘仨毿枰?,從而不能做出針?duì)新的優(yōu)化,為了解決當(dāng)前問(wèn)題,各大bundle廠商也都有各自的分析包的方案

          以vue-cli 為例

          "report":?"vue-cli-service?build?--report"
          復(fù)制代碼

          我們只需要在腳手架中提供以上命令,就能在打包時(shí)生成,整個(gè)包的分析文件

          image.png

          如上圖所示 在打包后就能分析出打包后的js 文件他包含什么組件,如此以來(lái),我們就能知道那些文件是沒(méi)必要同步加載的,或者走cdn的,通過(guò)配置將他單獨(dú)的隔離開(kāi)來(lái),從而找出性能的問(wèn)題

          利用chorme devtool 的代碼覆蓋率

          如下圖所示,

          image.png

          利用 devtool的代碼覆蓋率檢查就能知道那些js 或者css 文件的代碼沒(méi)有被使用過(guò),結(jié)合包內(nèi)容的分析,我們就能大概的猜出性能的瓶頸在哪里從而做相應(yīng)的特殊處理

          針對(duì)vue 的特殊優(yōu)化

          以上內(nèi)容都是通用的一些優(yōu)化手段,您在哪都能查到,只是我表達(dá)了一下做這些常規(guī)優(yōu)化的深層原因。能讓您更清楚的了解這些原因之后,在性能瓶頸的時(shí)候能游刃有余,而不是為了面試死記硬背,一到用的時(shí)候就不靈

          然后我司是vue啊,咱得上得vue 的手段

          圖片懶加載

          所謂圖片懶加載,就是頁(yè)面只渲染當(dāng)前可視區(qū)域內(nèi)的圖片,如此一來(lái),減少了其他圖片渲染數(shù)量,能大大提高SpeedIndexLCP的時(shí)間,從而提高分?jǐn)?shù)

          在vue中提起圖片懶加載插件,首推vue-lazyload[16]

          使用方式簡(jiǎn)單,功能豐富

          虛擬滾動(dòng)

          在一含有長(zhǎng)列表頁(yè)面中,你有沒(méi)有發(fā)現(xiàn)你是往下越滑越卡,此時(shí)虛擬滾動(dòng)就排上用場(chǎng)了, 他的基本原理就是只渲染可視區(qū)域內(nèi)的幾條數(shù)據(jù),但是模擬出正常滑動(dòng)的效果,因?yàn)槊看沃讳秩究墒莿∮騼?nèi)的數(shù)據(jù),在滑動(dòng)的時(shí)候他的性能就會(huì)有飛速提升

          在vue中比較好用的插件有兩個(gè)vue-virtual-scroller[17]vue-virtual-scroll-list[18]

          目前我司統(tǒng)一用的vue-virtual-scroll-list 他下拉的時(shí)候到了分頁(yè)的地方能加些loading提示

          vue 中的函數(shù)式組件

          在vue中我們知道組件的初始化是比較損耗性能的,大家可以去試一下,使用vue 直接渲染一個(gè)文字內(nèi)容,和直接渲染一個(gè)app.vue 組件他的分?jǐn)?shù)是略有不同的。

          但是當(dāng)有了函數(shù)式組件,這個(gè)問(wèn)題就迎刃而解了

          因?yàn)楹瘮?shù)是組件顧名思義他就是個(gè)函數(shù),說(shuō)白了就是個(gè)render函數(shù),他少了組件初始化的過(guò)程,省去了很多初始化過(guò)程的開(kāi)銷

          什么時(shí)候用函數(shù)式組件呢?

          當(dāng)你的組件中沒(méi)有業(yè)務(wù)邏輯只展示內(nèi)容時(shí),這時(shí)候函數(shù)式組件就派上用場(chǎng)了

          利用v-show 、KeepAlive 復(fù)用dom

          我們知道v-show是通過(guò)display 控制dom的展示隱藏,他并不會(huì)刪除dom 而我們?cè)谇袚Qv-show的時(shí)候其實(shí)是減少了diff的對(duì)比,而KeepAlive 則是直接復(fù)用dom,連diff 的過(guò)程都沒(méi)了,并且他們倆的合理使用還不會(huì)影響到初始化渲染。如此一來(lái)減少了js 的執(zhí)行開(kāi)銷,但是值得注意的是,他并不能優(yōu)化你初始化的性能,而是操作中的性能

          分批渲染組件

          在前面我們提到過(guò)SpeedIndex 的漸進(jìn)渲染是提高SpeedIndex的關(guān)鍵,有了這個(gè)前提,我們就可以分批異步渲染組件。先看到內(nèi)容,然后在渲染其他內(nèi)容

          舉個(gè)例子: