用實驗的思路優化webpack4專案編譯速度

祝學友發表於2018-09-01

最近開發的時候遇到一個問題:當專案越來越大的時候,webpack構建和編譯的速度變得很慢。儘管webpack4官方宣稱速度提高了90%以上,但實際使用的時候感覺速度和webpack2也差不多。我實在受不了熱載入的時候要等幾秒才能編譯好,於是就開始了優化之路。
最終優化的效果不錯,提速達到了80%以上。一路上按著以前做實驗的思維去優化,經歷了各種問題,相比於分享解決問題的方法,我反而更想分享下解決問題的思路。

先量化目標

一般做實驗之前,我們都要先定一個目標。
但是問題來了,雖然感覺很慢,但我不知道具體花了多少時間,慢是慢在哪裡,所以也沒辦法知道我們可以優化到什麼程度,優化也無從下手了。 於是首要的任務就是獲得webpack編譯過程的資訊。
在webpack的官方文件中找了一大圈後,發現了配置中devServer.stats屬性可以獲得編譯過程的完整資訊

stats: {
    timings: true,
    modules: false,
    assets: false,
    entrypoints: false,
    assetsSort: 'field',
    builtAt: false,
    cached: false,
    cachedAssets: false,
    children: false,
    chunks: false,
    chunkGroups: false,
    chunkModules: false,
    chunkOrigins: false,
    performance: true,
    errors: true,
    warnings: true,
},
複製程式碼

配成這樣之後得到了構建和編譯的時間:

用實驗的思路優化webpack4專案編譯速度
可以看到構建時間是40s,改動程式碼後編譯的時間是5.4s。
既然webpack4宣稱可以提速90%,那目前的情況肯定是有某些地方配置不當,我們就先定個目標,將編譯時間優化到1s以下。

猜測可能影響速度的因素

目前的專案是多入口的專案,每個入口之間沒有太多的關聯,但是卻放在了一起打包和編譯。我們平時開發的時候如果每次只編譯一個入口的檔案會不會快一點呢?
原本有三個入口,只載入一個入口的時候可以看到:

用實驗的思路優化webpack4專案編譯速度
構建和編譯的速度都加快了。變成了30s和3.4s。
既然有效,就寫幾個命令來方便開發不同的入口:

"scripts": {
    "dev": "webpack-dev-server  --config ./webpack.config/dev.js --hot --inline",
    "teacher": "app=teacher webpack-dev-server  --config ./webpack.config/dev.js --hot --inline",
    "student": "app=student webpack-dev-server  --config ./webpack.config/dev.js --hot --inline",
    "home": "app=home webpack-dev-server  --config ./webpack.config/dev.js --hot --inline",
    }
複製程式碼

在package.json中dev命令前加了一個app=XXX的引數

const teacherEntry = {
    ueditor: [
        'babel-polyfill',
        './src/common/UEditor/ueditor.config.js',
        './src/common/UEditor/ueditor.all.js',
        './src/common/UEditor/kityformula-plugin/addKityFormulaDialog.js',
        './src/common/UEditor/kityformula-plugin/getKfContent.js',
        './src/common/UEditor/kityformula-plugin/defaultFilterFix.js'
    ],
    teacher: ['./src/app/teacher/index.js'],
    teacherLogin: './src/app/teacherLogin/js/teacherLogin.js'
};
const studentEntry = {
    student: ['babel-polyfill', './src/app/student/index.js'],
    studentLogin: './src/app/studentLogin/js/studentLogin.js'
};
const homeEntry = {
    home: './src/app/home/index.js'
};
const entryObj = {
    teacher: teacherEntry,
    student: studentEntry,
    home: homeEntry
};

const entry = process.env.app
    ? entryObj[process.env.app]
    : Object.assign({}, teacherEntry, studentEntry, homeEntry);
複製程式碼

然後在webpack.base.config.js中加上了根據process.env.app的值來載入不同入口的程式碼,這樣就實現開發時跑不同的命令載入不同入口的效果了。

檢查webpack config中是否有影響效能的配置

接下來就用控制變數的方法去逐個去掉webpack的loader和plugin,然後觀察減少的時間了。

new WriteFilePlugin({
    test: /^((?!hot-update).)*$/
}),
複製程式碼

發現用到了一個寫入檔案的外掛,將所有檔案都寫到出口了,但實際上只需要寫入html檔案就行了,於是改成

new WriteFilePlugin({
    test: /\.html$/,
    useHashIndex: true
}),
複製程式碼

改後構建和編譯時間變成了30s和3s,有了一點點優化。
然後發現babel編譯js的時候沒有忽略掉node_modules目錄。

{
    test: /\.js$/,
    include: /(src|node_modules\/flv.js)/,
    exclude: /(node_modules)/,
    loader: 'babel-loader'
},
複製程式碼

加上後發現時間變成了27s和2.2s,已經是一開始的一半了。

對比別人的專案看有沒有可以借鑑的地方

配置看得差不多了,沒有再發現有問題的地方了,於是開始google查詢別人的優化方法。
後來找到了一個類似的專案用了happypack(多執行緒編譯)和cache-loader(快取),感覺優化效果還不錯:

new HappyPack({
    id: 'babel', // 對於loaders id
    loaders: ['cache-loader', 'babel-loader?cacheDirectory'], // 是用babel-loader解析
    threadPool: happyThreadPool,
    verboseWhenProfiling: true // 顯示資訊
}),
複製程式碼

加上了happypack和cache-loader之後可以看到第一次構建速度有了很大的提升,減少到了13.5s!
但是編譯的速度基本沒什麼變化,還是2s。革命尚未成功,同志仍需努力啊。

觀察編譯過程的資訊,尋找優化點

要進一步優化編譯的速度,只好去分析編譯的過程到底發生了什麼問題了。
將stats中的assets設定為true,觀察編譯的檔案:

用實驗的思路優化webpack4專案編譯速度
發現每一個js檔案都有一個相同大小的.map檔案,這樣豈不是會花費一倍的時間!?於是趕緊谷歌查一下.map檔案是何方神聖。 後來發現它來自於webpack配置中的devtool,主要用於除錯程式,官方配置說明如下:

用實驗的思路優化webpack4專案編譯速度
原來專案中一直是用了最慢的source-map!於是趕緊修改配置:

devtool: 'cheap-module-eval-source-map'
複製程式碼

改完後試了一下,構建和編譯時間縮短為11s和0.8s。

用實驗的思路優化webpack4專案編譯速度

用實驗的思路優化webpack4專案編譯速度
終於將編譯速度降到了1s一下,完成了最初定下的目標。

總結

經過這一路的優化之後,深感webpack的配置博大精深。每個專案可能都有不同的問題影響著webpack的速度,所以在此分享定位問題的思路,希望能幫助遇到類似問題的朋友一步步找到問題,提高速度。

Author: Brady

相關文章