概述
之前做了一個React專案和Vue的專案,在做完後不加任何優化的情況下,這2個專案的首屏載入平均時間居然達到了20+s,好一點能進10s,差一點快30s,完全不能忍,優化勢在必行,本文章記錄下Vue專案的優化過程,React專案的優化後續補上。
上圖是Vue專案的首頁,整個專案由vue-cli搭建,主要分為4個模組,見頁面左側導航欄,總體程式碼量不算太大,元件的話總共50個左右吧,專案結構見下圖,不算特別大的專案,但是首屏載入時間居然這麼慢。
首先得確定到底是哪裡導致了首屏渲染如此之慢?開啟Chrome Network皮膚,勾上Disable cache選項,重新整理頁面觀察資源載入情況,發現罪魁禍首就是webpack打包生成的app.js和vendor.js,其中vendor.js大小達到了1.2M,下載時間超過20秒,app.js也快到1M,而manifest.js不是很大。vendor.js主要是把node_modules裡所用到的modules都合併成一個js了,所以比較大.而我們也希望將業務程式碼和第三方引用分開打包。manifest.js
包含webpack的runtime程式碼和module manifest程式碼,作用是防止修改了程式碼但是沒有修改第三方庫檔案導致第三方庫檔案也打包的問題。
網上一番搜尋,發現優化點主要在如下幾個方面:
- 開啟gzip壓縮功能
- 引入CDN
- 路由懶載入
- 某些第三方元件按需載入而不是全部載入
- 較小的圖片資源用base64嵌入src中,減少http請求
具體優化
gzip壓縮
gzip是GNUzip的縮寫,最早用於UNIX系統的檔案壓縮。HTTP協議上的gzip編碼是一種用來改進web應用程式效能的技術,web伺服器和客戶端(瀏覽器)必須共同支援gzip。目前主流的瀏覽器,Chrome,firefox,IE等都支援該協議。常見的伺服器如Apache,Nginx,IIS同樣支援,gzip壓縮效率非常高,通常可以達到70%的壓縮率,也就是說,如果你的網頁有30K,壓縮之後就變成了9K左右
我後端是用的express,開啟gzip非常簡單,首先npm install compression安裝中介軟體,然後在app.js裡新增use使用即可:
var compression = require('compression');
var app = express();
app.use(compression())
複製程式碼
重啟服務,觀察網路皮膚裡面的response header,如果看到如下紅圈裡的欄位則表明gzip開啟成功
引入CDN
CDN(內容分發網路),是一種公共服務,他本身有很多臺位於不同地域、接入不同運營商的伺服器,而所謂的使用CDN實質上就是讓CDN作為網站的門面,使用者訪問到的是CDN伺服器,而不是直接訪問到網站。由於CDN內部對TCP的優化、對靜態資源的快取、預取,加上使用者訪問CDN時,會被智慧地分配到最近的節點,降低大量延遲,讓訪問速度可以得到很大提升
一個原則是儘量將比較大的第三方庫放到cdn上去以減少請求時間,在我的專案中,我將vue,vuex,vue-router,echarts都放到了cdn上,具體操作是開啟BootCDN 然後搜尋關鍵字並copy連結貼上到index.html的body閉合標籤前,如下圖
注意選取min.js(體積最小),然後在webpack.base.conf.js裡設定externals選項,目的是不打包這些選項,由於index.html中script的引入,比如vue就會有一個全域性變數Vue存在,因此這裡external的value就是Vueexternals: {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'echarts': 'echarts'
},
複製程式碼
cdn使用後優勢是巨大的,觀察network皮膚,時間幾乎都在50ms以下
路由懶載入
路由懶載入也叫延遲載入,即在需要的時候進行載入,隨用隨載。像vue這種單頁面應用,如果沒有應用懶載入,運用webpack打包後的檔案將會異常的大,造成進入首頁時,需要載入的內容過多,時間過長,會出現長時間的白屏,即使做了loading也是不利於使用者體驗,而運用懶載入則可以將頁面進行劃分,需要的時候載入頁面,可以有效的分擔首頁所承擔的載入壓力,減少首頁載入用時。
Vue官網的示例如下,採用非同步元件和webpack的code-splitting結合
因此在專案中,進入router的index.js中,將原來的import Comp from '@/component/xxx'
改為如下,vue-router的配置項還是保持不變
//非同步元件,路由懶載入
const BookMark = resolve => require(['@/components/BookMark'],resolve);
const HotBookMark = resolve => require(['@/components/HotBookMark'],resolve);
const ImportBookMark = resolve => require(['@/components/ImportBookMark'],resolve);
const Default = resolve => require(['@/components/Default'],resolve);
複製程式碼
打包後檢視js資料夾下的檔案,會多出上述檔案,每個進行懶載入的元件都會生成一個js,如下圖紅框內
第三方按需載入
比如本專案裡面使用的echarts,只用到了一個柱狀圖元件,其餘的都沒有用到,但是這樣import後打包時卻會把整個echarts都打入包內,造成空間浪費
import echarts from 'echarts'
複製程式碼
因此只需要import用到的元件即可,如下,這樣就可以減少很多不必要的體積
import echarts from 'echarts/lib/echars'
import 'echarts/lib/chart/bar'
import 'echarts/lib/component/legend'
import 'echarts/lib/component/title'
複製程式碼
圖片轉base64
小圖片可以轉為base64字串然後嵌入img的src中,節省http請求數量,webpack中用url-loader處理,limit控制了圖片轉base64的閾值,小於該值就轉base64
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
複製程式碼
優化後效能
經過上述優化後,首頁開啟時間迅速下降,DomContentLoaded用時不到1s,load完全載入用時不到4s,耗時較多的是幾張背景大圖,本來體積就大,後續考慮放到cdn上
這是最初的優化策略,後續繼續會進行深度優化。
ps:webpack-bundle-analyzer是神器,能夠有效分析出包占用的體積情況~下圖是最終優化後的包組成結構圖,最初打包的結構圖比下圖大很多,主要多了echarts,vue,vue-router,vuex模組
優化後的整個包gzip後大小喜人~