🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
需求背景
- 從第三方採購的vue2 + ElementUI實現的雲管平臺,乙方說2011年左右就開始有這個專案了(那時候有Vue了嗎,思考.jpg)。十幾年的專案,我何德何能可以擔此責任。裡面的程式碼經過多人多年迭代可以用慘不忍睹來形容,吐槽歸吐槽,混口飯吃,多爛的程式碼都得啃下去。
- 有一天領導找到我,問我怎麼回事,開啟頁面需要十幾秒時間也太慢了,後臺管理系統不要求首屏載入時間都沒有這麼慢,這個對外的系統超過1秒開啟時間,都會流失很多客戶。
如何看最佳化是否做到位?
1. Lighthouse谷歌外掛,從首頁開啟速度分析頁面的效能,並給出指標和打分。
- 如何使用Lighthouse?
- F12開啟控制檯 - Lighthouse
- 如下圖所示選擇,然後點選Anlyze page load就可以了
- 這裡只關注效能所以只勾選了Performance指標,其他可訪問性、SEO有需求一同檢測的自行勾選上。
2. webpack-bundle-analyzer外掛,分析程式碼打包大小的工具。
- 如何使用webpack-bundle-analyzer?
- npm install --save-dev webpack-bundle-analyzer安裝依賴
// webpack.config.js配置檔案 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { // ... 其他配置 ... plugins: [ new BundleAnalyzerPlugin() ] };
這樣在打包的時候會生成一個靜態網站檢視各個模組佔用的儲存空間大小
最佳化成果(有圖有真相) 🔥
LightHouse效能指標解釋: FCP:衡量的是開啟網頁後,瀏覽器渲染第一段 DOM 內容所用的時間 LCP:用於測量視口中最大的內容元素何時渲染到螢幕上。這粗略地估算出網頁主要內容何時對使用者可見。 因為要最佳化的頁面沒有像官網那樣有輪播圖佔據大量顯示位置的元素,所以我主要關注FCP,FCP解決LCP也會相應變快
1. 最佳化前首頁載入速度(FCP 15.8s)
2. 最佳化前體積(打包後大小80.5M)
Stat: 原始碼階段 Parsed: 經過webpack打包後的大小 Gzipped: 經過Gzip壓縮過後的大小,實際瀏覽器接收的大小,需要伺服器開啟Gzip壓縮 這裡的大小主要關注chunk-vendors.js和app.js。其他都跟首頁載入關係不大。下面的首屏程式碼大小是這兩個js程式碼大小之和。 chunk-vendors是引用的第三方庫如element-ui、echarts、vue等等打包後的程式碼。app.js是專案的程式碼。
- 這裡只放Gzipped的大小,全部都放圖太多了。
3. 最佳化後體積(三倍左右的程式碼體積減少,打包後大小減少64M!)
4. 最佳化後首頁載入速度(FCP快了13.6秒!)
- 看到這有人說,雖然是快了13.6秒,但是還是要2.2s,還是不能秒開,你的年終還是不保啊。
- 上面Lighthouse是不會用到快取去檢測效能的,為了有效他每次檢測都相當於首次全部載入,打包後的css、js靜態檔案都是可快取的。使用者第二次開啟時,是可以做到秒開。
怎麼最佳化 ✏️
1. 靜態檔案快取(js,css等),圖片和SVG進行壓縮或者替換。
- 這一點在最佳化官網的時候很有用!大多數官網程式碼都十分精簡,首屏載入慢大多都是因為輪播圖沒壓縮,檔案太大請求慢導致的,用壓縮工具壓縮一下,或者讓UI換個不失真佔用空間小的即可解決問題。
- 在用webpack-bundle-analyzer檢視包大小的時候,發現一個SVG竟然有1.5M!你敢信?而且SVG不會經過打包有大小變化,就是即使經過Webpack打包,Gzip壓縮,他也會佔用1.5M的的大小,最佳化前也才5.9M,所以1.5M佔比很大需要最佳化。
- 為什麼會有1.5M的SVG?
- 圖片可以轉SVG,但是隻是粗暴的將圖片的base64編碼塞到SVG裡面,體積增大33%
- 圖片未經壓縮,原圖比較大(甩鍋設計,哈哈哈)
- 最後改為使用靜態圖片引用,而且不是首頁需要用到的SVG,所以首頁程式碼大大減少
- 從下圖可以看到支付寶相關的SVG都比較大,最後都被我用阿里的iconfont用更小的svg替換了,減少了2.5M左右的大小!
2. 刪掉無用路由、引用的庫(實際未使用),然後啟用樹搖
- 刪掉無用路由很有用!加上樹搖,可以去掉很多程式碼。正常來說公司自研的專案,每一個路由都是必要。但是這是第三方經過多人多年迭代的專案,很多路由都是沒用的。
- 還有一些庫,在main.js檔案裡註冊了Vue的全域性元件,但是搜尋整個專案根本沒有用到,而且這個庫還挺大的。又可以減少一些程式碼。
- 這裡有個奇葩的點是,webpack@4.46.0的版本必須要指定mode: 'production'才會啟用樹搖!否則打包大小基本和原始碼大小一樣,參考上面最佳化前原始碼76.5M,打包後80.5M程式碼還多了一點!(也有可能樹搖開啟成功了,只是啟用了production其他最佳化減少了體積,有沒有大佬指導一下!)
3. 除了首頁元件以外,其他元件改為非同步元件,非同步載入。同一個路由的元件打包到一個js上。減少首屏載入時請求數太多。
import Home from '@views/Home.vue' const router = [ // 首頁不要非同步,才用匯入的方式打包到app.js,優先載入 { path: '/home', component: Home }, // 其他元件非同步載入,多個小元件可以打包到一起,減少請求數,程式碼分離要恰到好處 { path: '/xx', component: () => import(/* webpackChunkName: "xx" */ '@/views/xx.vue') } ],
4. 非同步載入首頁不需要用到的js和css檔案。
- 專案的index.html總有一些奇奇怪怪的js和css引入如下所示,會阻塞頁面的解析,我們在前端首頁解析完後(DOMContentLoaded事件)載入它們。
<link rel="stylesheet" href="./luckysheet/plugins/css/pluginsCss.css" /> <link rel="stylesheet" href="./luckysheet/plugins/plugins.css" /> <link rel="stylesheet" href="./luckysheet/css/luckysheet.css" /> <link rel="stylesheet" href="./luckysheet/assets/iconfont/iconfont.css" /> <script src="./luckysheet/plugins/js/plugin.js"></script> <script src="./luckysheet/luckysheet.umd.js"></script>
- 比如這樣
<script> document.addEventListener('DOMContentLoaded', () => { ;['./luckysheet/plugins/js/plugin.js', './luckysheet/luckysheet.umd.js'].forEach((item) => { const script = document.createElement('script') script.defer = true script.src = item document.body.appendChild(script) }) ;[ './luckysheet/plugins/css/pluginsCss.css', './luckysheet/plugins/plugins.css', './luckysheet/css/luckysheet.css', './luckysheet/assets/iconfont/iconfont.css' ].forEach((item) => { const link = document.createElement('link') link.rel = 'stylesheet' link.type = 'text/css' link.href = item document.head.appendChild(link) }) }) </script>
5. 此外還有服務端開啟http2、開啟Gzip壓縮,筆者最佳化之前已經開啟所以沒有對比,就不再贅述,實際上提升也非常大。
6. 最後效果
- 大小、請求數、載入時間都大大減少
- 注意最佳化後的時間chunk-vendors.js和app.js載入一共耗時1.59s,不是1.11+1.59,chunk-vendors的下載解析會阻塞app.js的下載解析,所以一共1.59s。
- 前:
- 後:
本文轉載於:https://juejin.cn/post/7359077652445806642
如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。