偶爾有一天覺得公司的 H5 項(xiàng)目在 build 的時(shí)候特別慢卦睹,看了下時(shí)間大約在 60 - 80s,而且由于需要在微信中看真機(jī)效果方库,所以需要經(jīng)常 build结序,于是去網(wǎng)上找找資料如何縮短這個(gè)加載時(shí)間。以下是我在優(yōu)化加載過程中遇到的一些坑及優(yōu)化方案纵潦。
從上圖中我們發(fā)現(xiàn)編譯耗時(shí)接近 80s徐鹤,而且仔細(xì)看還發(fā)現(xiàn) vendor.js 和 app.css 異常的大,這不僅導(dǎo)致我們在編譯階段很慢邀层,而且首屏響應(yīng)也會(huì)變慢返敬,怎么去解決呢?通過查了些資料我們開始著手優(yōu)化起來~
優(yōu)化方案
- 服務(wù)端開啟 gzip 壓縮
- webpack 配置優(yōu)化
- 按需加載第三方組件
項(xiàng)目配置
該 H5 商城基于 vue-cli 腳手架構(gòu)建寥院,引入 vue-router 劲赠、vuex、axios 搭建 SPA 應(yīng)用秸谢,UI 工具庫選用的是 iView凛澎、mint-ui,以及不少第三方組件估蹄。
服務(wù)端 Nginx 開啟 gzip
這個(gè)很重要塑煎,盡管我們現(xiàn)在已經(jīng)做了,但是需要提醒的是服務(wù)端 Nginx 開啟 gzip 壓縮能大幅度提高頁面加載速度臭蚁,前端需要配置 compression-webpack-plugin 插件最铁。這里注意根據(jù) webpack 版本來選擇 compression-webpack-plugin 插件版本,webpack4 使用 2.x 垮兑,webpack4 以下版本選擇 1.x
- config/index.js
module.exports = {
build: {
...
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: true, // 這里開啟 gzip冷尉,默認(rèn)為false
productionGzipExtensions: ['js', 'css'] // 只壓縮 js css 不壓縮圖片
}
}
- build/webpack.prod.conf.js
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
//增加瀏覽器CPU(需要解壓縮), 減少網(wǎng)絡(luò)傳輸量和帶寬消耗 (需要衡量系枪,一般小文件不需要壓縮的)
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240, // 達(dá)到10kb的靜態(tài)文件進(jìn)行壓縮 按字節(jié)計(jì)算
minRatio: 0.8 // 只有壓縮率比這個(gè)值小的資源才會(huì)被處理
})
)
}
由圖中可以看出网严,這里配置完后首屏加載速度提升是很明顯的,vendor 由 176kb 壓縮成61kb嗤无,app.css 由 1.22M 壓縮成186kb
震束,接下來我們可以看看如何通過配置 webpack 優(yōu)化打包速度。
1当犯、使用 webpack-parallel-uglify-plugin 插件來壓縮代碼
vue 默認(rèn)使用 UglifyJsPlugin 插件進(jìn)行代碼壓縮垢村,由于是單線程,壓縮效率較低嚎卫;我們可以使用 webpack-parallel-uglify-plugin 插件嘉栓,可以合理使用 CPU 資源宏榕,通過設(shè)置緩存能夠大大減少非首次構(gòu)建時(shí)間
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
...
// 注釋 webpack 提供的 UglifyJS 插件
//new UglifyJsPlugin({
// uglifyOptions: {
// compress: {
// warnings: false
// }
// },
// sourceMap: config.build.productionSourceMap,
// parallel: true
//}),
// 增加 webpack-parallel-uglify-plugin 來替換
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
compress: {
drop_debugger: true, // 去除生產(chǎn)環(huán)境的 debugger 和 console.log
drop_console: true
}
}
})
此時(shí)編譯發(fā)現(xiàn)打包時(shí)間縮短了不少,不錯(cuò)侵佃,總算看到點(diǎn)希望麻昼,接下來我們來嘗試下縮小打包后的文件大小。
2馋辈、減少打包文件數(shù)量
隨著項(xiàng)目的迭代抚芦,引入的第三方庫也會(huì)越來越多,每次 build 都會(huì)對所有文件進(jìn)行打包迈螟,耗時(shí)也會(huì)越來越久叉抡,不利于平時(shí)開發(fā)。這個(gè)時(shí)候我們可以通過減少一些不經(jīng)常更新的包來單獨(dú)打包答毫,我們有兩種方式褥民,第一種是通過 CDN 引入包然后配合 externals 配置的方式也可以,抽離第三方包洗搂,只打包業(yè)務(wù)代碼消返。第二種方式就是引入 DLL 文件,dllplugin 是 webpack DllPlugin耘拇、webpack 的簡稱撵颊,思路也是講一些第三方包抽離出來單獨(dú)打包,后面就不需要重新打包了驼鞭。
使用方法:
- 1、在 build 文件夾下新建
webpack.dll.conf.js
尺碰,內(nèi)容如下:
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
//這地方寫你想抽離的包挣棕,可以參考你的package.json文件下的 dependencies
vendor: [
'vue/dist/vue.common.js',
'vue-router',
'vuex'
]
},
output: {
//這地方寫你打包后生成文件的路徑
path: path.join(__dirname, "../dist/dll/"),
filename: '[name].dll.js',
library: '[name]_library'
},
plugins: [
//這個(gè)插件是重點(diǎn),用于打包上面entry里配置的包
new webpack.DllPlugin({
path: path.join(__dirname, '../dist/dll', '[name]-manifest.json'),
name: '[name]_library'
}),
new webpack.optimize.UglifyJsPlugin()
]
}
這里我們先抽離出 vue亲桥,vue-router 和 vuex
洛心,然后在 package.json 中 scripts
下加入該命令 "build:dll": "webpack --config build/webpack.dll.conf.js"
,執(zhí)行 yarn build:dll
命令將第三方依賴打包
可以看到在 dist 目錄下生成了一個(gè) dll 文件夾题篷,里面有 vendor-manifest.json 和 vendor.dll.js
這兩個(gè)文件词身,我們打開 build/webpack.base.conf.js
文件,編輯添加如下配置
const webpack = require('webpack');
module.exports = {
...
plugins: [
new webpack.DllReferencePlugin({
manifest: require('../dist/vendor-manifest.json')
})
]
...
}
接著我們只要在 index.html 中引入 vendor.dll.js
即可
<body>
<div id="app"></div>
<script src="./dist/dll/vendor.dll.js"></script>
</body>
接下來我們執(zhí)行 yarn build
發(fā)現(xiàn) vendor
文件由原來的 1.57M
變成了 931KB
番枚,看起來效果不錯(cuò)
這里我們只是預(yù)編譯了三個(gè)文件法严,當(dāng)我們把其他的第三方庫打包后看看效果如何
vendor 文件現(xiàn)在只有 270 kb 了,縮小了不知道多少倍~ 到這里葫笼,dll 的配置基本完事了深啤,我們還可以把 index.html
中的手動(dòng)引入的腳步改成自動(dòng)導(dǎo)入。
// webpack.base.conf.js
new webpack.DllReferencePlugin({
context: path.resolve(__dirname, '..'),
manifest: require('../dist/vendor-manifest.json')
}),
// 在htmlwebpack后插入一個(gè) AddAssetHtmlPlugin 插件反浓,用于將 vendor 插入打包后的頁面
new AddAssetHtmlPlugin({
filepath: require.resolve('../dist/dll/vendor.dll.js'),
includeSourcemap: false
})
一開始踩了很多坑茎毁,配置 dll 一直配不對,還是要細(xì)心~
然后刪除 index.html
引入的腳本即可淑掌。接下來我們可以繼續(xù)優(yōu)化一下 css 呈昔,從我們的 main.js 文件可以看到全局引入了很多 css挥等,我們可以通過 CDN 方式引入。
多說一句堤尾,cdn 引入其實(shí)原理和 dll 一樣肝劲,但是我們知道每一個(gè) cdn 資源也是需要走請求的,如果很多 cdn 的話還是要權(quán)衡一下哀峻,同理涡相,dll 文件如果太大,會(huì)影響首屏加載剩蟀,不能光圖打包爽催蝗。
CDN 引入全局樣式和字體庫
我們把 main.js 中一些可以從 cdn 上找到的樣式和字體都通過 cdn 引入到 index.html 中,然后再打包
我們發(fā)現(xiàn) css 文件也響應(yīng)縮小了一些育特,最終效果如下圖所示:
總結(jié)
從首次打包花了 80s 到現(xiàn)在 47s丙号,優(yōu)化了接近一半的時(shí)間,在研究 webpack 時(shí)也遇到不少問題缰冤,基本都是自己 Google 犬缨,不斷的翻閱文檔和相關(guān) issues。有的插件在 webpack3.x 和 webpack4.x 不盡相同棉浸,沒有做向下兼容怀薛,需要不斷地踩坑,比如這次的 add-asset-html-webpack-plugin
在 webpack3.x 下只能使用 2.x 版本迷郑,以及昨晚的 compression-webpack-plugin
插件在 webpack3.x 下只能使用 1.x 版本枝恋,這些東西其實(shí)勞心勞力但是又沒有什么用處,真心不建議跟 webpack 較勁嗡害。
項(xiàng)目優(yōu)化其實(shí)只是一個(gè)下策焚碌,更重要的是我們平時(shí)寫代碼需要多思考,提升效率和性能霸妹。我們寫的時(shí)候需要注意引入第三方組件以及樣式十电,不是多處用到的組件盡量按需加載,外部樣式走 CDN叹螟,公用邏輯盡量提出來鹃骂,不要寫完就完事了。就我們目前的商城罢绽,移動(dòng)端組件庫就有倆偎漫,查看請求時(shí)發(fā)現(xiàn)有幾張圖片都是 1.5M(編輯同學(xué)上傳時(shí)沒有壓縮),首頁第一次加載請求有200多個(gè)有缆,主要是圖片請求象踊,這塊如何優(yōu)化呢温亲?