優化 Vue 專案編譯檔案大小
與其說是優化 Vue,不如說主要是在 webpack 打包的配置中做些文章,使得 Vue 編譯後的檔案儘可能的小。以下介紹自己在專案中進行優化的過程,其中的內容也許並不適合於每個專案,但整體思路是差不多的。
定位問題
要想進行優化,首先我們得清楚問題所在。即:是哪些程式碼/依賴包導致最後的編譯檔案過大?
這裡,我們需要使用 webpack-bundle-analyzer
工具。修改 package.json
檔案,新增:
"analyze": "NODE_ENV=production npm_config_report=true npm run build"
然後執行:
npm run analyze
便會在瀏覽器中開啟一個頁面,展示編譯後的檔案大小及各部分內容大小。以下是專案在優化前的分析結果:
從圖中可以看出,最後編譯出的 vendor.js
檔案達到了 5MB,其中主要來自 echarts
。此外,由於 element-ui
在使用時已經注意到按需載入元件,所以可優化的部分不多;而 lodash
由於沒有按需載入,所以成為需要優化的另一個核心部分。
使用按需載入優化
這裡主要是對 lodash
進行優化。當我們在使用 lodash
時,如果使用:
import _ from 'lodash'
_.get(obj, 'key', 'default_value')
這種方式的話,則在編譯時會預設將 lodash
的全部內容進行編譯打包。
webpack 的介紹中確實提到了按需載入,但這個概念可能會出現理解上的偏差,下面我們舉例說明:
// 方法一:會導致載入全部的 lodash 庫
import _ from 'lodash'
_.get()
// 方法二:只會載入其中的 get 方法
import get from 'lodash/get'
get()
即在不新增其他外掛和配置的情況下,webpack 還做不到如此智慧。
想要實現在使用方法一的情況下,也能按照我們使用過的方法真正「按需載入」,則需要使用外掛並新增配置:
首先執行:
npm i --save-dev babel-plugin-lodash babel-cli babel-preset-es2015
然後修改 .babelrc
:
{ "plugins": ["lodash"], "presets": ["es2015"] }
之後修改 webpack.prod.conf.js
:
module: {
loaders: [{
'loader': 'babel-loader',
'test': /\.js$/,
'exclude': /node_modules/,
'query': {
'plugins': ['lodash'],
'presets': ['es2015']
}
}]
}
這之後便可以實現按需載入 lodash
了。重新進行分析,會發現 lodash 部分的大小已經可以忽略不計了。
對於其他的,如 Element-UI
之類的第三方庫,如果我們只使用到了為數不多的元件,建議查詢相應的按需載入外掛和配置方式,這樣可以極大的減少該部分編譯的大小。
路由懶載入
當我們配合 Vue-Router 構建單頁應用時,大量的元件會導致首屏載入緩慢,如官方文件所言:
當打包構建應用時,Javascript 包會變得非常大,影響頁面載入。如果我們能把不同路由對應的元件分割成不同的程式碼塊,然後當路由被訪問的時候才載入對應元件,這樣就更加高效了。
只需將原有的:
import Test from '../pages/test'
export default new Router({
routes: [
{
path: '/test',
name: 'test',
component: Test
}
]
});
改為:
const Test = () => import('../pages/test')
export default new Router({
routes: [
{
path: '/test',
name: 'test',
component: Test
}
]
});
注意首行的不同。
第三方庫懶載入
在實際開發中,可能存在這樣的場景:
在某個元件/檔案中需要使用 moment 第三方庫來進行時間處理,但其他元件根本用不到。
如果我們這樣引入 moment:
import moment from 'moment'
export default {
data () {
},
mounted () {
}
}
則該庫會合並在 vendor.js
中,造成首屏載入緩慢。
為了解決這個問題,我們可以改成以下引入方式:
export default {
name: '',
beforeCreate () {
import('moment').then(module => {
this.moment = module;
});
},
data () {
return {
moment: null
}
}
}
這種方式可以使得 moment
庫只在該元件使用處引入。注意,這種方式需要考慮「moment
呼叫時機與 moment
使用的先後問題」。
注:如果該元件是頁面級別的元件,則使用「路由懶載入」中的方法就可以了。
使用 CDN 外部載入
如上所示,echarts 模組佔了很大的部分,由於沒有找到 echarts 按需載入的外掛,這裡我們通過外部引用的方式來減少編譯的大小。
首先,我們修改 index.html
,從 CDN 中引入 echarts 檔案:
<script src="https://cdn.bootcss.com/echarts/3.7.0/echarts.min.js"></script>
注意,如果需要地圖元件,也需要一併引入。
這之後我們需要刪除所有 import echarts from 'echarts'
的程式碼,即不再通過這種方式引入 echarts。
但問題來了,如果這麼做的話,webpack 在打包的時候會發現 echarts 變數不存在而停止編譯。解決辦法是,我們需要在 webpack 配置中告知編譯器,對於 echarts 變數使用了引入外部資源的方式。需要修改 webpack.base.config.js
:
module.exports = {
externals: {
"echarts": "echarts"
},
}
這之後我們便可以直接使用 echarts
這一變數而不會導致編譯錯誤了。
當我們將所有采用之前方式引入 echarts 的程式碼刪除或註釋之後,再次進行分析,會發現編譯大小少了很多。
經過以上兩步,原本 5M 的編譯檔案變為了 1.67 M。
這之後,我們還可以根據分析結果,針對性地進行優化。如更換時間庫為更輕量級的 spacetime
等。
伺服器端開啟 gzip
使用 gzip 可以進一步壓縮檔案,使得伺服器傳遞給瀏覽器的檔案是經由壓縮之後的,待瀏覽器收到之後再解壓縮。要使用這一方式,需要伺服器端的支援,這裡以 Nginx 為例。
在 nginx.conf
中,新增如下配置:
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
#gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/javascript;
gzip_vary off;
之後重新整理頁面( 注意禁用快取 ),觀察 js、css 等資原始檔的請求中是否包含 Content-Encoding: gzip
,如果存在,則表明 gzip 已成功。
注意,在 gzip_types
中規定了哪些請求型別會使用 gzip 進行壓縮。對於沒有使用 gzip 的資原始檔,可將其 Content-type
型別加入 gzip_types
之中。
伺服器端渲染
注意,在以上對打包過程的優化中,受影響的主要是 vendor.js
檔案中第三方庫的部分( gzip 方法會影響全部資原始檔 )。
如果我們想繼續進行優化,就需要考慮伺服器端渲染了。
Vue 的作用機制實際是使用 js 向 html 中掛載元件,如果我們能夠將這一過程放在伺服器端進行,就可以不再向瀏覽器傳輸一部分驅動檔案,從而進一步減少瀏覽器所需的檔案大小。不過這一過程需要伺服器的額外支援,有興趣的同學可以參考:例項 PK ( Vue服務端渲染 VS Vue 瀏覽器端渲染 )。
參考
莉莉安 · 3月23日樓主~
您的第三方庫懶載入方式
我按照引入後,在頁面中寫此元件,會報錯說沒有註冊該元件
贊
有具體一點的使用方式嗎
對於某庫(如 moment)採用懶載入的引入方式時,需要在真正使用時等待該庫檔案已經下載到瀏覽器。比如:
export default {
name: '',
beforeCreate () {
import('moment').then(module => {
// 這一過程會導致該庫檔案從遠端開始下載,只有下載完成才能使用該庫
// 可以開啟瀏覽器的 network 看到這一過程
this.moment = module;
});
},
data () {
return {
// 初始化時該值為空
// moment 懶載入中該值也為空
// 只有當懶載入完成才有值
moment: null
}
}
}
使用這種方式載入第三方庫,需要手動判斷懶載入是否完成。如果覺得麻煩,可以直接藉助路由懶載入。被納入路由懶載入的元件,其引用的第三方庫也會隨之實現懶載入。