從實踐中尋找webpack4最優配置

筆者最近在準備給fle-cli升級到webpack4版本,以爲有必要將探索過程的經驗分享給你們,遂決定寫這篇文章。(不知道fle-cli看這裏css

webpack4是大趨勢,升級是必然的,那麼爲何如今才升級?html

緣由有如下幾個方面:node

  • 剛發佈的版本還不穩定,潛在風險大,而目前版本已更新到4.8.3,基本處於穩定;
  • webpack社區工具未徹底跟上節奏,好多工具都得本身搞,勞心勞力(其實主要就是懶哈哈);
  • webpack自己及社區工具存在或多或少的問題,未經時間沉澱,維護成本高。

然而如今,筆者認爲以上這些已經成熟,是時候來一波升級了。webpack

前言

本文不會講解webpack配置的每一個細節點,由於這些官方文檔均可以看到。筆者會挑一些難以理解的新概念、可能會碰到的問題,以及筆者總結下來的優化方案來分享,但願能夠給你們帶來一些幫助。git

配置

mode

mode是webpack4新增的參數選項,它的值有3個:development、production、none,可以幫助咱們加載一些默認配置,none即不加載默認配置。下面將對應的默認配置列出來供你們參考,以避免重複配置。github

developmentweb

注重提高代碼構建速度和開發體驗npm

module.exports = {
  cache: true,
  devtools: "eval",
  plugins: [
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
  ]
}

prodution瀏覽器

提供代碼優化,如壓縮、做用域提高等緩存

var UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  plugins: [
    new UglifyJsPlugin(/* ... */),
    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
    new webpack.optimize.ModuleConcatenationPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ]
}

optimization

這個選項也是webpack4新增的,主要是用來自定義一些優化打包策略。

minimizer

在production模式,該配置會默認爲咱們壓縮混淆代碼,但這顯然知足不了咱們對於優化代碼的訴求。下面筆者分享一套自身實踐總結下來的配置及解釋:

var UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      // 自定義js優化配置,將會覆蓋默認配置
      new UglifyJsPlugin({
        exclude: /\.min\.js$/, // 過濾掉以".min.js"結尾的文件,咱們認爲這個後綴自己就是已經壓縮好的代碼,不必進行二次壓縮
        cache: true,
        parallel: true, // 開啓並行壓縮,充分利用cpu
        sourceMap: false,
        extractComments: false, // 移除註釋
        uglifyOptions: {
          compress: {
            unused: true,
            warnings: false,
            drop_debugger: true
          },
          output: {
            comments: false
          }
        }
      }),
      // 用於優化css文件
      new OptimizeCssAssetsPlugin({
        assetNameRegExp: /\.css$/g,
        cssProcessorOptions: {
          safe: true,
          autoprefixer: { disable: true }, // 這裏是個大坑,稍後會提到
          mergeLonghand: false,
          discardComments: {
            removeAll: true // 移除註釋
          }
        },
        canPrint: true
      })
    ]
  }
}

UglifyJsPlugin這款插件相信你們也是常常用到,這裏再也不多說,這裏的亮點是過濾掉自己已是壓縮的js文件,可以提高咱們的編譯效率以及避免二次混淆壓縮而形成的未知bug。

OptimizeCssAssetsPlugin這款插件主要用來優化css文件的輸出,默認使用cssnano,其優化策略主要包括:擯棄重複的樣式定義、砍掉樣式規則中多餘的參數、移除不須要的瀏覽器前綴等,更多優化規則看這裏。前文咱們提到這裏有個大坑,相信你已經察覺到了,沒錯,就是這貨把咱們經過autoprefixer加好了前綴給移除了。筆者查閱了許多資料,依舊沒有找到滿意的答案,沒辦法,只有硬着頭皮去源碼中找答案了,因而便有了這段配置autoprefixer: { disable: true },禁用掉cssnano對於瀏覽器前綴的處理。

runtimeChunk

分離出webpack編譯運行時的代碼,也就是咱們先前稱爲manifest的代碼塊,好處是方便咱們作文件的持久化緩存。它能夠設置多種類型的值,具體能夠看這裏,其中single即將全部chunk的運行代碼打包到一個文件中,multiple就是給每個chunk的運行代碼打包一個文件。

咱們能夠配合InlineManifestWebpackPlugin插件將運行代碼直接插入html文件中,由於這段代碼很是少,這樣作能夠避免一次請求的開銷,可是新版插件的配置和以前有些不太同樣,接下來詳細講解一下如何配置。

var HtmlWebpackPlugin = require('html-webpack-plugin')
var InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin')

module.exports = {
  entry: {
    app: 'src/index.js'
  },
  optimization: {
    runtimeChunk: 'single'
    // 等價於
    // runtimeChunk: {
    //   name: 'runtime'
    // }
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'fle-cli',
      filename: 'index.html',
      template: 'xxx',
      inject: true,
      chunks: ['runtime', 'app'], // 將runtime插入html中
      chunksSortMode: 'dependency',
      minify: {/* */}
    }),
    new InlineManifestWebpackPlugin('runtime')
  ]
}

這段配置會產生一個叫作runtime的代碼塊,和老版本不一樣的是,咱們並不須要在html模版中添加<%= htmlWebpackPlugin.files.webpackManifest %>,只需將runtime加入chunks便可。這裏有一個點要注意,InlineManifestWebpackPlugin插件的順序必定要在HtmlWebpackPlugin以後,不然會致使編譯失敗。

splitChunks

終於要講到重頭戲了,也是筆者我的認爲最難以理解的一個配置項。webpack4移除了CommonsChunkPlugin插件,取而代之的是splitChunks。

咱們先來看下默認配置:

splitChunks: {
  chunks: "async",
  minSize: 30000,
  minChunks: 1,
  maxAsyncRequests: 5,
  maxInitialRequests: 3,
  automaticNameDelimiter: '~',
  name: true,
  cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      priority: -10
    },
    default: {
      minChunks: 2,
      priority: -20,
      reuseExistingChunk: true
    }
  }
}

默認配置只會做用於異步加載的代碼塊,它限制了分離文件的最小體積,即30KB(注意這個體積是壓縮以前的),這個是前提條件,而後它有兩個分組:屬於node_modules模塊,或者被至少2個入口文件引用,它纔會被打包成獨立的文件。

爲何要限制最小體積呢?由於webpack認爲小於30KB的代碼塊分離出來,還要額外消耗一次請求去加載它,成本過高,固然這個值也不是隨便意淫出來的,而是通過大量的實踐總結獲得的,筆者我的認爲這是一個很是好策略。

maxAsyncRequests(最大的異步請求數)和maxInitialRequests(最大的初始請求數)這兩個參數則是爲了限制代碼塊劃分的過於細緻,致使大量的文件請求。

可是隻分離異步代碼塊顯然知足不了咱們的需求,所以接下來筆者分享一套相對來講比較優雅的分離打包配置:

splitChunks: {
  cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      name: 'vendors',
      minSize: 30000,
      minChunks: 1,
      chunks: 'initial',
      priority: 1 // 該配置項是設置處理的優先級,數值越大越優先處理
    },
    commons: {
      test: /[\\/]src[\\/]common[\\/]/,
      name: 'commons',
      minSize: 30000,
      minChunks: 3,
      chunks: 'initial',
      priority: -1,
      reuseExistingChunk: true // 這個配置容許咱們使用已經存在的代碼塊
    }
  }
}

首先是將node_modules的模塊分離出來,這點就再也不累述了。異步加載的模塊將會繼承默認配置,這裏咱們就不須要二次配置了。

第二點是分離出共享模塊,筆者認爲一個優雅的項目結構,其公共代碼(或者稱爲可複用的代碼)應該是放置於同一個根目錄下的,基於這點咱們能夠將src/common中的公用代碼提取出來。

固然你還能夠有另一種選擇,將後綴爲.js且使用次數超過3次的文件提取出來,可是筆者不建議這個作,由於這不利於持久化緩存,新增或刪除文件都有可能影響到使用次數,從而致使原先的公共文件失效。

文末

原先還想着講一下css插件部分的配置,限於篇幅,本文就再也不進行講解說明了,感興趣的小哥哥小姐姐能夠在這裏翻看源碼:webpack4-test

順便在這裏推薦一款好用的全局通用腳手架fle-cli:旨在幫助咱們從複雜繁瑣的編譯配置中解放出來,全身心地投入業務開發中,提升開發效率;同時它也是真正意義上的全局腳手架,區別於市面上其餘的全局腳手架,它不會在項目工程中生成各類編譯配置文件,也不會給你安裝一系列編譯的依賴包,這意味着你的項目工程能夠很是乾淨純粹。

相關文章
相關標籤/搜索