扔掉 cli,webpack工程輕量化配置實戰

前言

以前有用 webpack4與babel7改造基於vue-cli2生成的工程模板,介紹文章在此。以後經過一些實踐,除去了cli工具相對複雜的配置結構,提供輕量化版本的配置方案。之因此說是輕量化,是相對於Vue、React等框架提供的官方cli工具而言的。並非說這些cli工具很差,它們自己提供了開箱即用的良好特性,又集成了不少提高開發體驗的插件,確實能下降框架使用的門檻。但也正是由於工具高度集成,配置高度抽象,致使生成的webpack配置文件結構略顯複雜。對於一個技術選型已趨穩定的前端團隊來講,好比通常只會使用一種css預處理方案,以及相對固定的插件集成,因此徹底能夠固定某些配置選項,從而輸出一個輕量的工程配置方案。這也有利於靈活更改或升級某些依賴,從而進一步提高開發體驗及輸出性能。javascript

明確需求

在開始配置以前,仍是要明確自身的需求。對於前端應用來講,使用webpack做爲工程化工具,咱們但願除了靜態文件server,babel編譯等基本需求外,還能知足如下這些體驗及要求:css

開發階段

  • 利用HMR實現模塊熱加載,包括css模塊
  • 儘量加快首次全量編譯及rebuild速度
  • 在編譯結果中能清晰看到生成的bundle包及chunk文件

生產輸出

  • 壓縮優化輸出的js、css包,以及其餘靜態資源
  • 合理配置hash,減小非必要更新
  • 合理配置分包策略,控制輸出包的大小及數量

配置方案

本文是以Vue框架應用爲例,應用示例用的就是vue-cli生成的工程模板,能夠clone 代碼倉庫 到本地運行。React技術棧的同窗也請留步,webpack的配置方案基本是一致的,無非就是babel配置有差別。簡化後的方案只在build目錄下有3個配置文件:webpack.base.js, webpack.dev.js, webpack.prod.js。其中 devprod 分別是開發和生產構建的webpack config文件,在package.json中能夠配置以下npm scripts用於開發和生產構建:html

"scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.js",
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.js"
}

兩個文件都繼承了webpack.base.js,此文件包含了主要配置項,下面就方案的關鍵配置作下註解。前端

webpack.base.js

環境變量、Helper方法

  • devMode 變量用於區分開發與生產構建
  • resolve 方法用於拼接絕對路徑,base目錄爲工程根目錄
const devMode = process.env.NODE_ENV !== 'production';

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

入口和上下文

  • 設定上下文爲工程根目錄
  • 入口爲 src 目錄下的 main.js,此處路徑相對於上下文。能夠視項目實際狀況配置多個入口。
context: resolve(''),
entry: {
    app: './src/main.js'
}

輸出 output

  • path指定輸出目錄爲dist,全部構建生成的資源都放入此目錄
  • filename 命名輸出的bundle文件,這裏根據devMode區分,生產構建會加上對應bundle的chunkhash ,這裏不用 hash,是須要精確控制每一個bundle的緩存,以知足咱們對於生產環境加載優化的需求。尤爲是多入口的狀況下,若是隻更新其中一個業務bundle,其餘bundle的hash不變,從而長效利用緩存,減小沒必要要的資源下載。 [name] 會被入口的命名替換,該項目即爲app
  • chunkFilename 命名輸出的chunk文件,上面的filename是用來控制入口對應的bundle文件,而這個主要是用於分包(splitChunks)以及懶加載等產生的chunk文件,也使用chunkhash[name] 會被分包定義的名稱替換。
  • 此處輸出的bundle文件前面加上了目錄js,後面的 css、圖片等資源也對應加上了目錄,這能夠根據項目規範來定,沒有強制約定。不論是否有前置目錄,都是相對於 output.path目錄的。
  • publicPath也是一個關鍵配置,會影響到按需加載或外部資源加載,特別是css中引用的圖片、字體等資源,若是設置不正確,有可能致使沒法加載,受限篇幅與主題,就不展開講了。
output: {
  path: resolve('dist'),
  filename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash].js',
  chunkFilename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash].js',
  publicPath: '/',
}

解析 resolve

主要設置模塊如何被解析,包括別名配置,能夠提高必定的構建效率,具體看配置,不作展開。vue

optimization: 分包配置 splitChunks

根據項目規模和資源加載性能要求進行合理的分包配置,其中涉及到的參數與原理都會相對複雜,本文也不作展開。本方案裏給到的是能夠知足中小型應用的通用策略:分出 common包與vendor包。java

  • common包是針對於多入口應用的,本示例工程其實不起效。對於多入口應用,提取共同依賴模塊到common包,能夠減少每一個業務bundle的size,同時也能利用緩存的優點,優化加載性能。還能夠經過minChunks等參數進一步控制分包粒度。
  • vendor包是將全部在node_modules中的第三方模塊所有打包成一個chunk。這樣作的一個好處是,相對於業務bundle,第三方依賴的變化頻度較低,chunkhash能夠穩定較長時間。但若是你的項目持續集成是每次從新全量安裝構建,那此策略的效果不會很好,由於npm依賴更新頻度過高了(主要是minor和patch版本升級,除非是鎖定版本)。若是單個包過大,也能夠經過maxSize等參數進一步拆分。
optimization: {
  splitChunks: {
    chunks: 'async',
    name: true,
    cacheGroups: {
      common: {
        name: 'common',
        chunks: 'initial',
        minChunks: 2
      },
      vendor: {
        name: 'vendor',
        test: /[\\/]node_modules[\\/]/,
        chunks: 'all'
      }
    }
  }
}

module.rules

模塊處理規則配置,使用恰當的loader處理各類模塊。node

  • 由於是vue應用,因此須要使用vue-loader處理.vue文件 。loader的配置基本取自vue-cli,具體看源碼,須要詳細瞭解的能夠查看官方文檔
  • js 模塊使用babel-loader進行處理,babel的選項能夠在.babelrcpackage.json中的babel節點單獨配置。注意一下 userBuiltInscorejs的配置,因爲babel編譯配置不是本文重點,也再也不展開。
"presets": [
  [
    "@babel/preset-env",
    {
      "modules": false,
      "targets": {
        "browsers": [
          "> 1%",
          "last 2 versions",
          "not ie <= 8"
        ]
      },
      "useBuiltIns": "usage",
      "corejs": 3
    }
  ]
],
  • css 模塊,根據項目使用的預處理方案配置 test及預處理 loader,而後是css-loader,最後是用MiniCssExtractPlugin.loader將代碼提取到css文件中,需配合下面的plugins配置。生成的css文件命名規則與output配置相似,注意生產構建用的是該插件提供的contenthash,也是利用緩存優化。mini-css-extract-plugin已經支持了內置 HMR,開發階段啓用hmr option便可,在這以前還須要使用css-hot-loader來配合實現css的 HMR。其原理也比較簡單, 就是經過從新加載生成的css文件進行樣式覆蓋。另外,若是是純前端應用(見下節解釋),開發階段也能夠考慮使用style-loader來實現 HMR。
{
  test: /\.(sa|sc|c)ss$/,
  use: [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        hmr: devMode,
      },
    },
    'css-loader',
    // 'postcss-loader',
    // 'sass-loader',
  ],
},
  • 圖片、字體、其餘媒體資源等直接用url-loader處理,配置limit選項可優化請求數,另外命名直接帶上hash

插件 plugins

  • vue應用需引入 vue-loader/lib/plugin
  • 上文提到的MiniCssExtractPlugin配置
MiniCssExtractPlugin({
  filename: devMode ? 'css/[name].css' : 'css/[name].[contenthash].css',
  chunkFilename: devMode ? 'css/[id].css' : 'css/[id].[contenthash].css',
}),
  • 因爲本示例應用是純前端應用(引用資源的入口是html文件),因此還要使用HtmlWebpackPlugin插件來處理入口html文件的資源引用注入修改,咱們無需關心不一樣環境構建輸出的資源包路徑命名差別,插件會自動注入資源引用代碼。但若是不是純前端應用,好比使用了 Node應用框架或其餘後端語言框架的模板,則須要考慮如何引入輸出後的資源路徑了,但具體的實踐可能須要另開話題分享了。

webpack.dev.js

webpack.dev.js 是開發環境的webpack config 文件,使用webpack-merge繼承 webpack.base.js ,內容主要是開發階段所需的一些特定配置。webpack

  • mode設爲development ,會默認使用DefinePlugin設置process.env.NODE_ENV值爲development,可在源碼中獲取區分環境用;另外還會默認啓用幾個開發階段所需的插件。
  • devtool 用於指定source map格式,此示例值爲cheap-module-eval-source-map,首次構建與從新構建速度相對較快,以及支持行級別的源碼映射品質,是開發階段的推薦格式。
  • devServer主要是webpack-dev-server的配置,包括host,端口,啓用hmr、gzip壓縮等配置。這裏使用了portfinder解決開發環境可能的端口衝突,能夠根據實際狀況決定是否使用。另外使用copy-webpack-plugin插件替代contentBase,能夠用於非 webpack 編譯處理的靜態資源伺服訪問。
  • devServer.stats 能夠配置終端中編譯輸出的顯示信息,如下配置的搭配能夠看到每次編譯後的bundle、chunk包列表,又不會有大串的編譯過程信息干擾,具體能夠根據實際需求進行調整。
devServer: {
    stats: {
      colors: true,
      builtAt: true,
      cached: true,
      cachedAssets: true,
      modules: false,
      children: false
    }
  }

效果是這樣的:git

dev output

webpack.prod.js

webpack.prodjs 是生產環境的webpack config 文件,也使用webpack-merge繼承了 webpack.base.js 的主要配置。在這裏設置mode值爲 production,一樣使用DefinePlugin設置process.env.NODE_ENV值爲production,使用TerserPlugin等插件對生產構建輸出包進行壓縮優化。另外引入optimize-css-assets-webpack-plugin插件,對輸出的css bundle也作優化處理。
也可使用 babel-minify-webpack-plugin插件進一步壓縮經babel編譯的代碼,但從實際使用來看,此插件帶來的壓縮效果並不明顯,編譯耗時卻是增長很多,能夠看具體狀況來決定是否使用。github

小結

至此,輕量版本的webpack工程配置已經完成。能夠拉取倉庫代碼在本地運行體驗,工程效果基本是與 vue-cli 生成版本一致的,不過對比vue-cli 的整個應用工程配置, 去除了單測、e2e測試,以及編譯錯誤桌面提醒等插件的集成,能夠根據實際需求再行配置。

配置工具化

對於須要維護多個項目的團隊,爲了能讓webpack工程配置能儘量通用,能夠考慮將配置方案封裝成一個npm包,抽象部分配置項做爲可變參數,好比publicPath,通常是不一樣應用不一樣值。在應用裏可使用webpack-merge,對一些配置進行覆蓋或增長,會有更好的靈活性。就如文章開頭所提,一個團隊內的模塊處理方案基本是統一的,因此無需抽象過多配置項,不然又走回 cli 工具的老路了。

結語

本文從實戰出發,提供了一個相對普適的輕量化webpack工程配置方案。受篇幅所限,沒有對一些配置項作過多解釋,若是須要了解某個配置細節,能夠查詢相關文檔或文章。有興趣的同窗也能夠閱讀webpack源碼做深刻了解。

相關文章
相關標籤/搜索