webpack優化不完全指南

Zore_發表於2018-01-02

基礎篇

最基本的一個webpack配置

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
        entry: __dirname + "/app/main.js", //已多次提及的唯一入口檔案
        output: {
            path: __dirname + "/build",
            filename: "bundle-[hash].js"
        },
        devtool: 'none',
        devServer: {
            contentBase: "./public", //本地伺服器所載入的頁面所在的目錄
            historyApiFallback: true, //不跳轉
            inline: true,
            hot: true
        },
        module: {
            rules: [{
                    test: /(\.jsx|\.js)$/,
                    use: {
                        loader: "babel-loader"
                    }
                }, {
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        fallback: "style-loader",
                        use: [{
                            loader: "css-loader",
                            options: {
                                modules: true,
                                localIdentName: '[name]__[local]--[hash:base64:5]'
                            }
                        }, {
                            loader: "postcss-loader"
                        }],
                    })
                }
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin(),
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new 一個這個外掛的例項,並傳入相關的引數
        }),
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("style.css")
    ]
};

複製程式碼

接下來基於這份配置進行一個優化:
首先我們得區分開發環境、生產環境以及測試環境(這個在國內用的公司少) 實現思路(參考vue-cil),通用配置可以設定一個base檔案然後exports,在其他的配置檔案中require進來。

上面這個配置中new webpack.optimize.UglifyJsPlugin()就可以放入webpack.prod.conf.js中,因為程式碼壓縮醜化只需要在生產環境中使用,在開發過程開啟的話,不僅編譯時間加長還不利於除錯。

module與entry、output這個就可以放到webpack.base.cong.js檔案中,這些配置是通用的不管在生產環境還是開發環境中都是用得到的。

devServer則是在開發過程中需要啟動一個伺服器然後再開啟熱替換功能。

  • 在module中也可進行些優化,例如在字尾為.js檔案的處理中新增:

include: [resolve('src'), resolve('test')]

這樣loader載入器就只會匹配src/與/test目錄下的檔案,至於node_modules目錄下的檔案則跳過,假如沒加這一限制條件的話,node_modules下那麼多的js檔案,想想都覺得恐怖,並且別人釋出出來的包檔案一般都是經過babel轉成ES5標準的。

  • 新增alias,如:

alias: {
'@': resolve('src')
}

這樣當webpack遇到import('@xxx')的時候就會直接在src/目錄下去尋找,能節省不少的目錄查詢時間

進階篇

多頁面優化

當我們的專案是多個頁面的時候,我們需要用到HtmlWebpackPlugin外掛,

for(let i = 0; i< html.length; i++) {
  plugins.push(
    new HtmlWebpackPlugin({
        filename:fileName,
        template: path.join('./src/html/', fileName),
        inject: true,
        minify: {
          removeComments: true,
          collapseWhitespace: false,
          removeAttributeQuotes: true
        },
        chunksSortMode: 'dependency',
        chunks: ['manifest', 'vendor', cur]
      }))
  )
}
複製程式碼

有多少個頁面就push多少個HtmlWebpackPlugin進去,至於頁面的獲取我們可以通過walk-sync來做到,通過獲取目錄下的入口檔案來判斷有多少個頁面存在。
我在優化過程中遇到個坑,當webpack從2.x升級為3.x的時候,

compilation.getStats().toJson()

這個函式的執行時間會大大加長,我看到其實在issue裡頭有人提出來了,不過作者並沒有給合併到master中去。還有點就是:當頁面是多頁面的時候每一次小的改動都會把所有的html檔案重新生成一次,針對這兩個優化我在Githubfork了下html-webpack-plugin,併發布了html-webpack-plugin-multihtml。我們的專案一共是有49個頁面之前rebuild是花費5-7s ,優化後保持在1s左右。

提取公共程式碼CommonsChunkPlugin

有些類庫如utils, bootstrap之類的可能被多個頁面共享,最好是可以合併成一個js,而非每個js單獨去引用。這樣能夠節省一些空間。這時我們可以用到CommonsChunkPlugin,我們指定好生成檔案的名字,以及想抽取哪些入口js檔案的公共程式碼,webpack就會自動幫我們合併好。

new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      chunks: Object.keys(entrys),
      minChunks: function (module, count) {
        return (module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0 && count > 8)
      }
}),
複製程式碼

這個是針對多頁面優化的,CommonsChunkPlugin會自己計算當一個類庫被引用的次數超過8次,就把它打包至vendor中,這樣就不必全打包至一個index.js裡頭

externals提取常用庫

externals 配置選項提供了「從輸出的 bundle 中排除依賴」的方法。相反,所建立的 bundle 依賴於那些存在於使用者環境(consumer's environment)中的依賴。
externals主要是防止將某些import的包打包到bundle中,而是在執行時(runtime)再去從外部獲取這些擴充套件依賴

配置也挺簡單的接下來貼下程式碼:

index.html中引入

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
複製程式碼
webpack.config.js中配置

externals: {
  jquery: 'jQuery'
}
複製程式碼

這樣就能在程式碼中通過import引用了:

import $ from 'jquery';
複製程式碼

uglifyPlugin多執行緒壓縮程式碼

在webpack的預設情況下,都是單執行緒跑的,有個叫webpack-uglify-parallel的外掛,它能夠充分呼叫計算機的資源,利用計算機的多核特性進行程式碼壓縮。
使用方法也挺簡單的, 把之前webpack.prod.conf.js中的

new webpack.optimize.UglifyJsPlugin({
  compress: {
    warnings: false,
    drop_console: true
  },
  comments: false
}),
複製程式碼

替換為

var os = require('os');
var UglifyJsParallelPlugin = require('webpack-uglify-parallel');

module.exports = {
    /// ... rest of config
    plugins: [
        new UglifyJsParallelPlugin({
            workers: os.cpus().length, // usually having as many workers as cpu cores gives good results
            // other uglify options
        })
    ]
}
複製程式碼

即可 GitHub地址:webpack-uglify-parallel

Happypack

happypack 是 webpack 的一個外掛,目的是通過多程式模型,來加速程式碼構建。目前我們專案中已經配置並且上線了,效果還可以,從最初的40s減少到30s的編譯時間。
配置按照GitHub上的來,也不難

var HappyPack = require('happypack');
var happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });

// 省略其餘配置
module: {
  loaders: [
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract('style',path.resolve(__dirname,'./node_modules', 'happypack/loader') + '?id=less'
        )
      }
    ]
  },
  plugins: [
      new HappyPack({
        id: 'less',
        loaders: ['css!less'],
        threadPool: happyThreadPool,
        cache: true,
        verbose: true
      })
  ]
複製程式碼

我在優化我們專案的webpack遇到了兩個坑,一個就是上面提到的,webpack2升級3編譯時間增多,另一個就是devServer在低版本安卓機型上跑步起來。devServer這個使用"webpack-dev-server": "2.7.1"即可,因為2.9.x版本使用的是es6的寫法安卓4.1以下不支援,ie9以下也不支援。

相關文章