webpack實戰——打包優化【上】

前言

本篇介紹一些webpack優化的配置方法,目的有二:

  1. 打包速度更快
  2. 輸出資源更小

注意:在軟件工程領域有一條十分重要的功能經驗,不要過早優化。在項目初期不要看到一個可以優化的點就去做優化,這樣極有可能會增加尤其開發及維護的複雜度,並且從整體效果看,優化效果不會太理想。

1. HappyPack

HappyPack是一個通過多線程來提升webpack打包速度的工具

1.1 工作原理

在打工過程中,非常耗時的一個工作是使用loader將各種資源進行轉譯處理,例如常見的使用babel-loader將ES6+語法代碼轉譯爲ES5等。代碼轉移的工作流程如下:

  1. 從配置中獲取打包入口;
  2. 匹配loader規則,並對入口模塊進行轉譯;
  3. 對轉譯後的模塊進行依賴查找;
  4. 對新找到的模塊重複步驟2)和3),直到沒有新的依賴模塊。

從上述步驟中可以看出,從步驟2)到步驟4)其實是一個遞歸的過程,webpack需要一步步地獲取更下一級的資源然後逐個進行轉譯。爲什麼逐個?問題就在於webpack是單線程的。而HappyPack便將這裏作爲切入口,它的核心特性是可以開啓多個線程,並行的對不同模塊進行轉譯,這樣便更加充分的利用計算機資源來提升打包速度。

1.2 工程目標

HappyPack顯然更加適用於轉譯任務比較繁重的工程,當我們把類似babel-loadre,ts-loader等遷移到HappyPack之上後,一般會有比較不錯的效果,而對於sass-loader等本身消耗時間並不太多的功能則效果一般。

1.3 單個loader優化

以babel-loader爲例:

// 初始webpack.config.js
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    presets: ['react'],
                }
            }
        ]
    }
}

使用HappyPack:

// 使用HappyPack:
const HappyPack = require('happypack');
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'happypack/loader'
            }
        ]
    },
    plugin: [
        new HappyPack({
            loaders: [
                {
                    loader: 'babel-loader',
                    options: {
                        presets: ['react']
                    }
                }
            ]
        })
    ]
}

在module.rules中,我們使用happypack/loader替換掉了原來的babel-loader,而在plugins中添加了HappyPack插件,將原來的bebel-loader及其配置插入進去即可。

1.4 多個loader優化

在使用HappyPack對多個loader進行優化時,需要爲每一個loader配置一個id,否則HappyPack無法知道rules與plugins的對應關係

這裏以babel-loader及ts-loader爲例:

// webpack.config.js
const HappyPack = require('happypack');
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'happypack/loader?id=js'
            },
            {
                test: /\.ts$/,
                exclude: /node_modules/,
                loader: 'happypack/loader?id=ts'
            }
        ]
    },
    plugin: [
        new HappyPack({
            id: 'js',
            loaders: [
                {
                    loader: 'babel-loader',
                    options: {
                        // babel配置
                    }
                }
            ]
        }),
        new HappyPack({
            id: 'ts',
            loaders: [
                {
                    loader: 'ts-loader',
                    options: {
                        // ts配置
                    }
                }
            ]
        })
    ]
}

除上述配置之外,可以查閱官方文檔查看更多的配置,例如線程數,debug模式等。

2. 縮小打包作用域

從宏觀角度看,提升性能的方式總結爲兩種:

  1. 增加資源: 使用更多的CPU和內存,用更多的計算能力來縮短任務執行時間;
  2. 縮小範圍: 針對任務本身,去除冗餘流程,不做重複性工作或使其簡單化;

而上面我們所瞭解的HappyPack明顯屬於增加資源,那接下來介紹從縮小範圍的幾個方案。

2.1 exclude 和 include

在前面章節(預處理器(loader)【上篇】)中,介紹過exclude和include,在配置loader的時候一般都會對其進行配置。對於JS來說,一般需要把node_modules目錄排除掉,另外當exclude和include規則有重疊部分時,exclude優先級更高。

那對於此處,我們使用include讓babel-loader只生效於源碼目錄:

// webpack.config.js
...
module: {
    rules: [
        test: /\.js$/,
        include: /src\/scripts/,
        loader: 'babel-loader'
    ]
}

2.2 noParse

有些庫我們希望webpack完全不要去解析,那此時可以使用noParse對其進行忽略,如:

// webpack.config.js
// ...
module.exports = {
    noParse: {
        // fullPath是絕對路徑,如 /User/me/app/webpack/noparse/lib/lodash.js
        return /lib/.test(fullPath);
    }
}

如上配置將忽略所有lib目錄下的資源解析。

2.3 IgnorePlugin

exclude和include是確定loader的規則範圍,noParse是不去解析但仍會打包到bundle中,那接下來介紹一個插件——IgnorePlugin,他可以完全排除一些模塊,被排除的模塊即使被引用也不會被打包進資源文件中。一般作用於排除一些庫相關文件。

一個由庫產生的額外資源我們用不到但沒辦法去掉時,可以考慮使用此方法處理。

例,一個日期時間處理的相關插件Moment.js,爲了做本地化會加載許多語言包,但一般我們只會用到本地的語言包而不會使用其他地區的語言包,而語言包會佔用很大體積,這時可以使用IgnorePlugin來做處理:

// webpack.config.js
...
plugins: [
    new webpack.IgnorePlugin({
        resourceRegexp: /^\.\/locale$/, // 匹配資源文件
        contextRegExp: /moment$/    // 匹配檢索目錄
    })
]

2.4 Cache

我們在使用某些loader時會有一個cache的配置項,用來在編譯代碼後同時保存一份緩存,在執行下一次編譯前會優先檢查源碼文件是否有變化,沒有則直接使用緩存結果,也就是上次編譯的結果。這樣一來,只有在發生變化時編譯變化了的文件,對於整體而言也屬於一種優化處理。

小結

本篇從多線程打包縮小打包作用域兩個方面入手,對webpack打包層面做出優化。下一篇描述從動態鏈接庫思想死代碼檢測方面繼續深入探究打包層面的深度優化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章