這篇文章總結一下我過往項目的web性能優化,主要是就項目中如何發現性能問題,優化如何解決,談一下性能優化。把學習的過程跟你們分享一下,共同窗習。web的性能監測工具我用的是Chrome的Performance 面板和Lighthouse。javascript
回想起之前工做被產品拉進小黑屋,要我優化目前項目組的3個項目的性能問題。說體驗起來比較卡。我帶着激動地心,顫抖的手打開項目跑一下分數,廣泛15-60分。由於咱們項目是數據可視化的項目。追求的是什麼?酷炫吊炸天!!!!各類3D的地圖,酷炫的動畫,複雜的交互。優化……好吧,打工人打工魂。哦豁,優化好像依然沒有達到預期的效果,通過不懈的努力仍是能有質的提高。由於公司項目是內網的(公司安全防範超過嚴格,啥也看不了),我是半行代碼也拷不出來,也不敢貼出來呀,我儘可能把問題製造在dome裏,也方便觀察和解決。css
拓展插件,沒有安裝的安裝看一下。若是有錯誤的地方但願你們提出來,以便我能及時修改~html
Lighthouse生成的是一個報告,會給你的頁面跑出一個分數來。 分別是頁面性能(performance)、Progressive(漸進式 Web 應用)、Accessibility(可訪問性)、Best Practices(最佳實踐)、SEO 五項指標的跑分。甚至針對咱們的性能問題給出了可行的建議、以及每一項優化操做預期會幫咱們節省的時間。這份報告的可操做性是很強的——咱們只須要對着 LightHouse 給出的建議,一條一條地去嘗試,就能夠看到本身的頁面,在一秒一秒地變快。vue
打開項目跑一下分數,看看都有什麼問題(分數每次測會有誤差,並且跟網速也有關係,因此不用太在乎分數,關心一下Lighthouse給咱們的建議)還有我的以爲在生產環境測會比本地靠譜java
嗯,這個這個,HTTP/2的優勢我就很少說了,你們能夠去了解一下。15年發表到如今已經很成熟了,可使用起來~react
考慮一下按需引入和CDN(這個在實際項目中會比較複雜,由於代碼比較多。分析問題比較難)webpack
我隨便加了幾個第三方庫,寫了幾個頁面。首先看看大小,明明打包出來壓縮的是493k的怎麼加載時1643k呢nginx
去看看nginx配置,哦豁,示範的時候,我把開啓Gzip註釋了,放開放開,以後看看正常了。web
目測打包出來1M多的chunk.js也是有點大,webpack廣泛優化:切割一下打包文件,不要所有打包到一塊兒。全局引入了echarts和element咱們把這兩個js分開打包, 使用optimization.splitChunksnpm
結果
我把壓縮釋放看看,點開Coverage面板,刷新看看。能夠看到那些是關鍵資源(紅色非關鍵,藍色關鍵資源)。
點擊查看詳細加載狀況,紅色的就是沒有使用的代碼,可是打包壓縮後的代碼,咱們通常都看不出來是人仍是鬼。因此咱們儘可能對代碼進行切割,不只能夠減小大文件加載的時間,也能夠明確問題所在。
咱們能夠處理一下第三方的js,能看到echarts和elemenet-ui加載的大小和實際使用的大小有出入,通常Lighthouse用超過20 kb的未使用代碼標記每一個JavaScript文件。咱們改變一下element-ui的引入方式,目前是全局引入,咱們可使用按需引入,由於目前我只用到了Button,咱們就只須要引入Button。優化後打包大小明顯變小。
若是項目使用的組件比較多,按需引入不方便,必須全局引入。這時候考慮一下,由於第三方的庫咱們不會常常去更新的,可使用cdn的方式引入,就減免打包的大小。
配置(我vue也不怎麼用,你們參照參考官網配置就好,注意的是vue必需要先加載了,否則element-ui會報錯。若是開發環境使用**min.js可能沒法調試) 使用(再也不須要import,其餘正常使用就好)
對於不常改變的靜態資源好比說css、image等能夠進行緩存,針對緩存也總結了一下,能夠看看
打開ngnix配置文件,把緩存配上(這裏粗暴把js和css緩存了,實際項目根據實際須要配置緩存)
瀏覽器的渲染器過程將您的代碼轉換爲用戶能夠與之交互的網頁。默認狀況下,渲染器進程的主線程一般處理大多數代碼:它解析HTML並構建DOM,解析CSS並應用指定的樣式,並解析,評估並執行JavaScript。主線程還處理用戶事件。所以,每當主線程忙於執行其餘操做時,您的網頁就可能沒法響應用戶交互,從而致使不良的體驗。
看看dome的例子,主要渲染時間在Style&&Layout,說明咱們的重排重繪時間佔了主要時間,那咱們就想辦法減小重排,這部分在performance面板看比較直觀,下面performance會講到這個例子的處理,這裏跳過。
Google對咱們的建議是:
這個會比較籠統,只能根據項目一點一點改變, 樣式、佈局和渲染下面performance會介紹到
上面有提過 Coverage Tool
圖片能夠看出這個頁面其實根本就不使用到element-ui css, 咱們能夠設置延遲加載非關鍵CSS
webpack設置 html-critical-webpack-plugin
效果
原理
- FOIT是瀏覽器在加載字體的時候的默認表現形式,也就是在字體加載過程當中,頁面是看不到文本內容的。在現代瀏覽器中,FOIT會致使這種現象出現至多3秒。FOIT會致使不好的用戶體驗,這是咱們須要儘可能去避免的.
- FOUT意思是在字體加載過程當中使用默認的系統字體,字體加載完後顯示加載的字體,若是超過了FOIT(3s)字體還沒加載,則繼續使用默認的系統字體。
- swap告訴瀏覽器使用字體的文本應當即使用系統字體顯示。自定義字體準備好後,它將替換系統字體。能夠避免在大多數現代瀏覽器中使用FOIT(並不是全部主流瀏覽器都支持font-display: swap)
當JavaScript執行時間超過2秒時,Lighthouse將顯示警告。執行時間超過3.5秒時,審覈將失敗
建議(這些webpack都有相關的配置):
這個涉及因素比較多,考慮從多方面入手,參考下面方法:
減小網絡負載方法
PRPL
好比:vuecli3.x or 4.x默認打包以後,部署到服務器上的項目,會對靜態資源的標籤上默認加載preload或者prefetch屬性(preload主要用於預加載當前頁面須要的資源;而prefetch主要用於加載未來頁面可能須要的資源)
預加載&&延遲加載
<link rel="preload" as="style" href="css/style.css">
prefetch :是一種利用瀏覽器的空閒時間加載頁面未來可能用到的資源的一種機制;一般能夠用於加載非首頁的其餘頁面所須要的資源,以便加快後續頁面的首屏速度;
延遲加載 :是一種根據須要而不是預先加載資源的策略。這種方法在初始頁面加載期間釋放了資源,並避免了加載從未使用過的資產。
若是您在網頁上加載許多圖像,請在加載頁面時推遲全部摺疊如下或設備視口以外的圖像(請參閱使用lazysizes延遲加載圖像)。
在瀏覽器能夠呈現任何內容以前,它須要將HTML標記解析爲DOM樹。若是遇到任何外部樣式表(<link rel="stylesheet" />
)或同步JavaScript標記(<script src="main.js"></script>
),HTML解析器將暫停。 腳本和樣式表都是渲染阻塞資源,這些資源會延遲FCP,從而延遲LCP。推遲使用任何非關鍵的JavaScript和CSS來加快網頁主要內容的加載。 減小CSS阻斷時間:
延遲加載圖片應該常常會用到,就不示範了 vue-lazyload / lazysizes.min.js
這個很好理解就不寫案例了————
始終在圖像和視頻元素上包括width和設置height尺寸屬性。以確保在瀏覽器開始獲取圖像以前在頁面上分配了足夠的空間。這將最大程度地減小回流和從新佈局。
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons" />
或者設置height:auto 自適應保真比例也能夠
lighthouse生成一個報告有些參數來源於performance,相對比lighthouse的分數和建議,performance用於記錄和分析咱們的應用在運行時的全部活動。它呈現的數據具備實時性、多維度的特色,能夠幫助咱們很好地定位性能問題。
好比下圖,咱們看到style/Layout耗時很是誇張,可是我定位不到準確的地方,這個時候咱們用performance面板看看 Performance總體分析
名詞 | 解析 | 詳細 |
---|---|---|
FP (First Paint) | 首次繪製 | 標記瀏覽器渲染任何在視覺上不一樣於導航前屏幕內容以內容的時間點 |
FCP (First Contentful Paint) | 首次內容繪製 | 標記瀏覽器渲染來自 DOM 第一位內容的時間點,該內容多是文本、圖像、非空白canvas或SVG 甚至 元素. |
LCP (Largest Contentful Paint) | 最大內容渲染 | 表明在viewport中最大的頁面元素加載的時間. LCP的數據會經過PerformanceEntry對象記錄, 每次出現更大的內容渲染, 則會產生一個新的PerformanceEntry對象. |
DCL (Dom Content loaded) | 當 HTML文檔被徹底加載和解析完成以後,DOMContentLoaded 事件被觸發,無需等待樣式表、圖像和子框架的完成加載 | |
FMP(First Meaningful Paint) | 首次有效繪製 | |
L (onLoad) | 加載完成 | 當依賴的資源, 所有加載完畢以後纔會觸發. |
TTI (Time to Interactive) | 可交互時間 | 指標用於標記應用已進行視覺渲染並能可靠響應用戶輸入的時間點. |
TBT (Total Blocking Time) | 頁面阻塞總時長 | TBT彙總全部加載過程當中阻塞用戶操做的時長,在FCP和TTI之間任何long task中阻塞部分都會被彙總 |
FID (First Input Delay) | 首次輸入延遲 | 指標衡量的是從用戶首次與您的網站進行交互(即當他們單擊連接,點擊按鈕等)到瀏覽器實際可以訪問之間的時間 |
CLS (Cumulative Layout Shift) | 累積佈局偏 | 總結起來就是一個元素初始時和其hidden之間的任什麼時候間若是元素偏移了, 則會被計算進去。具體的計算方法可看這篇文章 《Cumulative Layout Shift (CLS)》 |
SI (Speed Index) | 指標用於顯示頁面可見部分的顯示速度, 單位是時間 |
名詞 | 解析 | 詳細 |
---|---|---|
response | 響應 | 用戶輸入以後是否能在100ms以內響應 這裏的輸入包括點擊按鈕、切換表單控件等,但不包括觸摸滑動或滾動(50ms內完成較好) |
animation | 動畫 | 最近手機圈很流行將屏幕刷新率提高爲90hz,這裏hz就是幀率,90hz就是每秒有90幀,一幀就是一個畫面。每秒看到的畫面越多,咱們就會感到越流暢,(每10ms內產生一幀較好) |
idle | 瀏覽器空置狀態 | 利用空閒的時間完成一些推遲的工做。推遲的工做應分爲50ms的多個塊進行。(儘量增長空閒時間) |
load | 加載 | 5s加載完成而且能夠交互 |
【第3如下配置都是用來模擬手機、慢網絡下使用的】
名詞 | 解析 |
---|---|
no recordings | 就是每一次的檢測報告,能夠根據每一次的檢測報告,去進行性能優化的對比 |
Screenshots | 是用來查看在每一個時間段界面的變化 |
Memory | 存儲調用棧的大小,在不一樣時間段的不一樣大小; |
Disable Javascript samples | 禁用 javascript 調用棧,關閉javaScript樣本減小在手機運行時的開銷,模擬手機運行時勾選 |
Enable advanced paint instrumentation (slow) | 記錄渲染事件的細節,選擇frames中的一塊,能夠看到區域四多了個Layers |
Network | 網絡模擬,能夠模擬在3G,4G等網絡條件下運行頁面; |
CPU | 用來查看電腦的性能問題,主要爲了模擬底CPU下運行性能 |
HEAP | JavaScrip 執行的時間分佈。 |
區域2:網頁性能總覽圖(overview) |
名詞 | 解析 |
---|---|
FPS | 每秒幀數,是用來分析動畫的一個主要性能指標,對於動畫而言標準是保持在60FPS。綠色越高越好,出現紅色則表示FPS低(這就是你爲啥以爲頁面卡頓了),你能夠在區域三Frames中看到具體的FPS值(見下面第二圖) |
CPU | 處理各個任務花費的時間,選擇一段CPU統計能夠在區域四的Summary看到統計表格 Scripting 腳本 Rendering 渲染 Painting 繪製 Loading 加載 ldle 閒置 |
NET | 每條彩色橫槓表示一種資源。橫槓越長,檢索資源所需的時間越長。 每一個橫槓的淺色部分表示等待時間(從請求資源到第一個字節下載完成的時間)。 |
區域3:線程面板 | |
名詞 | 解析 |
Frames | 幀線程,鼠標懸浮綠色塊能夠看到fps |
Main | 主線程,負責執行Javascript, 解析HTML/CSS, 完成繪製。 能夠看到主線程調用棧和耗時狀況,每一個長條都是一個事件,懸浮能夠看到耗時和事件名 x軸指時間: 最上面的第一條就是事件觸發的地方,直到結束,這條線是最長的 y軸指調用棧:上面的event調用了下面的子event,越到下面數量越少(瀑布) |
Raster | Raster線程,負責完成某個layer或者某些塊(tile)的繪製。光柵化線程池,用來讓 GPU執行光柵化的任務 |
Interactions | 用來記錄用戶交互操做,好比點擊鼠標、輸入文字、動畫等 |
Timings | 用來記錄一些關鍵的時間節點在什麼時候產生的數據信息,諸如 FP、FCP、LCP 等 |
Compositor | 合成線程的執行記錄,用來記錄html繪製階段 (Paint)結束後的圖層合成操做 |
區域4:統計面板
名詞 | 解析 |
---|---|
Summary | 統計圖:展現各個事件階段耗費的時間 |
Bottom-Up | 排序:能夠看到各個事件消耗時間排序 (1)self-time 指除去子事件這個事件自己消耗的時間 (2)total-time 這個事件從開始到結束消耗的時間(包含子事件) |
Call Tree | 調用棧:Main選擇一個事件,表示事件調用順序列表(從最頂層到最底層,而不是隻有當前事件) |
Event Log | 事件日誌 (1) 多了個start time,指事件在多少毫秒開始觸發的 (2) 右邊有事件描述信息 |
一、下面這張圖,動畫看起來是有點卡頓的問題的,利用performance測試一下,能夠看到Main面板裏面展開看到不少Task右上角都有紅色的三角形,說明是個Long Task,注意Animation Frame Fired事件右上角的紅色三角形圖標,該紅色三角圖標是這個事件有問題的警告。
選中其中一個Animation Frame Fired 查看詳細信息,能夠看到有問題的代碼在第幾行,點擊代碼會跳到指定代碼
再放大能夠看到Animation Frame Fired有不少紫色的Layout模塊,右上角也有紅色三角形。點擊選中Forced reflow查看信息,看到形成卡頓的緣由是不少元素進行不斷的迴流和重繪。
看一下代碼,有1000個元素在不斷經過他的offsetTop改變寬(若是你電腦性能比較好,能夠改爲5000個,卡頓效果更佳明顯)。當你要用到像這樣的屬性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 時,須要經過即時計算獲得,因此也進行迴流(重排)。咱們沒法避免讀取屬性,可是不用每個元素讀取完offsetTop再立刻賦值,咱們能夠先批量讀取完屬性後再賦值.能夠利用開源的fastdom來解決這個問題(能夠去看看它的源碼,沒有多少行)
2.使用fastdom優化的代碼(讀寫分離)
3.優化先後效果對比
優化前 優化後
下面是須要拖拽線條位置改變方格大小(固然了,只寫了線條拖拽功能),再performance上看到有不少layout Shift(佈局偏移)
放大Mian
看代碼,利用定位改變left的位置進行移動的,這樣會形成重排。咱們能夠利用transform代替,減小重排。
優化後 效果
參考文獻:
Google 的 web.dev/