背景
上篇講了首屏優化,具體文章詳見移動spa商城優化記(一)---首屏優化篇,這次來分享一下打包速度的一些優化經驗,因為在實際專案開發中,隨著專案的不斷增大,依賴項不斷增多,我們會發現webpack打包速度會越來越慢,有時候npm run一下可能出去上個廁所的時間都夠了,在這快速發展的時代,這麼拖節奏的事情絕不允許,本篇還是以公司spa商城為例,詳細介紹一下優化打包速度方面的經驗。
開始
公司這個專案的起手模板是vue-cli的webpack模板,專案完成大概有將30個頁面,未經優化前打包時間如圖:
大概兩分鐘左右,是不是出去上個廁所的時間都夠了。接下來開始優化,優化主要分四方面:減少打包檔案數量,減少不必要的功能開銷,優化打包方式,升級打包工具。
1.減少打包檔案數量
指導思想:要打包的東西少了速度自然就快了。
1.dll
webpack.DllPlugin + webpack.DllReferencePlugin這組外掛應該是借鑑了dll的思想所以叫dllplugin這個名字,目的就是將vue,vue-router,react,jquery等等這些體積較大,專案中不常更新的第三方包抽離出來單獨打包,然後告訴webpack這些包我之前已經打包過了,你每次打包直接用就行了,不用每次再去重新打包一次了。
使用方法:
- build資料夾下新建一個webpack.dll.conf.js,內容可以這樣寫:
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry:{
//這地方寫你想抽離的包,可以參考你的package.json檔案下的dependencies
vue:['vue','vue-router']
},
output:{
//這地方寫你打包後生成檔案的路徑
path:path.join(__dirname,"../src/dll"),
filename:'[name].dll.js',
library:'[name]'
},
plugins:[
//這個外掛是重點,用於打包上面entry裡配置的包
new webpack.DllPlugin({
path:path.join(__dirname,"../src/dll",'[name]-manifest.json'),
name:'[name]',
}),
new webpack.optimize.UglifyJsPlugin()
]
}
複製程式碼
- 執行
webpack --config build/webpack.dll.conf.js
打包生成抽離的公共包。
此時src下應該能看到dll目錄及生成的公共包js及json。 - 正常webpack配置檔案裡配置DllReferencePlugin進行關聯
plugins: [
new webpack.DllReferencePlugin({
//這裡寫上一步打包出的json路徑
manifest:require('../src/dll/vue-manifest.json')
})
......
]
複製程式碼
現在,再次打包就可以了,你提取出的第三方包越多,打包速度優化的越明顯。如果想讓打包出的html可以自動引入第二步打包出的dll.js檔案,可以使用add-asset-html-webpack-plugin或者自己修改HtmlWebpackPlugin的配置實現打包出的html內自動引入dll.js檔案。
關於HtmlWebpackPlugin詳細配置可以看這篇文章:
html-webpack-plugin用法全解
關於add-asset-html-webpack-plugin詳細配置可以看這篇文章:
add-asset-html-webpack-plugin配置
另外使用cdn引入包然後加 externals配置的方式也可以,思路和dll一致,都是抽離第三方包,只打包業務程式碼,只不過這種方式第三方包直接引用cdn上的。
2.減少不必要的resolve
- 看下webpack的每個rule下面include裡面是不是有多餘的resolve,或者看看有沒有把node_modules資料夾exclude掉。
- 用到babel-loader的地方,記得設定cacheDirectory,以利用bable的快取。
- resolve裡面的extensions可以刪除不必要的字尾名自動補全,減少webpack的查詢時間,比如
extensions: ['.js', '.vue', '.json','.scss','.css'],
寫這麼多自動補全寫程式碼的時候是省了字尾名了,但是這需要webpack打包時去自動查詢字尾增加了時間開銷。 - import時多使用完整路徑而不是目錄名,如
import './components/scroll/index.js'
而不是import './components/scroll
,減少webpack的路徑查詢。
2.去除不必要的功能開銷
指導思想:減少打包過程中要做的額外的工作
我們的目的就是優化打包速度,那麼與這個無關的不必要的功能可以先暫停掉,用到時再開。
關掉source map。
sourcemap有以下幾個配置值:
eval: 生成程式碼 每個模組都被eval執行,並且存在@sourceURL
cheap-eval-source-map: 轉換程式碼(行內) 每個模組被eval執行,並且sourcemap作為eval的一個dataurl
cheap-module-eval-source-map: 原始程式碼(只有行內) 同樣道理,但是更高的質量和更低的效能
eval-source-map: 原始程式碼 同樣道理,但是最高的質量和最低的效能
cheap-source-map: 轉換程式碼(行內) 生成的sourcemap沒有列對映,從loaders生成的sourcemap沒有被使用
cheap-module-source-map: 原始程式碼(只有行內) 與上面一樣除了每行特點的從loader中進行對映
source-map: 原始程式碼 最好的sourcemap質量有完整的結果,但是會很慢
複製程式碼
當我們不需要除錯時,可以關掉sourcemap或降低sourcemap的級別來加快打包的速度。
3.優化打包方式:並行
指導思想:並行打包速度當然快。
1.UglifyJSPlugin並行
這個比較好配置,UglifyJSPlugin外掛下加一個屬性parallel設為true即可。
new UglifyJSPlugin({
parallel: true
......
})
複製程式碼
還可以設定打包快取,具體見下面的配置
2.Happypack
Happypack通過多程式模型,來加速程式碼構建。
比如說以前使用vue-loader處理vue檔案,以前是序列處理,現在利用happypack可以並行使用vue-loader處理vue檔案。
- 首先安裝HappyPack
npm install --save-dev happypack
- 修改webpack.base.config.js
const HappyPack = require('happypack');
const vueLoaderConfig = require('./vue-loader.conf')
exports.module = {
rules: [
{
test: /\.vue$/,
//vue-loader替換為happypack/loader,如果遇到vue檔案就用happypack,id指定為vue
loader: 'happypack/loader?id=vue'
},
{
test: /\.js$/,
//babel-loader替換為happypack/loader,如果遇到js檔案就用happypack,id指定為js
loader: 'happypack/loader?id=js'
}
......
]
};
exports.plugins = [
new HappyPack({
id:'vue',
//同時開多少執行緒進行打包,也可以用ThreadPool控制
threads: 4,
loaders: [{
//這是真實的處理loader,具體配置和rules裡原本的一致,options也照搬過來就行
loader:'vue-loader',
options:vueLoaderConfig
}]
}),
new HappyPack({
id: 'js',
threads: 3,
loaders: [{
loader:'bable-loader',
}]
})
......
];
複製程式碼
經過以上配置,我們就可以使用happypack愉快的進行打包了。
更多高階配置可以看文件:Happypack文件
另外,想了解happypack原理的可以看淘寶團隊的這篇文章:happypack 原理解析
4.升級打包工具
指導思想:鳥槍換大炮,升級打包工具。
- 升級node
- 升級webpack
webpack4比3快,3比2快,推薦webpack至少升到3以上,4還不太穩定,強行升級坑較多。 - 升級各種loader
很多時候,大神們在冥冥之中已經幫我們在底層做了優化,我們根本不需要做什麼配置,我們只需要升級工具即可,當然,升級的同時要保證專案的健壯性。
最後
一圖勝千言,這是經過優化後的打包時間:
打包時間從原來的的120s減少到了現在的34s,優化率70%以上。再也不用每次npm run 一下就先去上個廁所了。。。