1. 程式人生 > >webpack學習之路(七)

webpack學習之路(七)

生產環境構建

本節我們將深入瞭解一些應用於構建網站和應用的最佳實踐和工具。

配置

開發環境和生產環境的構建目標差異很大。在開發環境中我們需要具有強大的實時重新載入和模組熱替換功能的source map和localhost server 。而在生產環境中,我們的目標變成了更小的輸出包、更輕量的source map和更好的資源優化來減少載入時間。遵循邏輯分離我們推薦為每個環境編寫獨立的webpack配置。

雖然我們對生產環境和開發環境做了略微區分,但是遵循不重複原則DRY(don't repeat yourself)還是保留一個通用配置。為了整合這些配置我們需要一個webpack-merge外掛。有了通用配置我們就不用在特定環境的配置中重複程式碼了。

安裝webpack-merge外掛並把之前成型了的程式碼分離:

npm install --save-dev webpack-merge

project

  webpack-demo
  |- package.json
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js |- /dist |- /src |- index.js |- math.js |- /node_modules 

webpack.common.js

+ const path = require('path');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
+
+ module.exports = { + entry: { + app: './src/index.js' + }, + plugins: [ + new CleanWebpackPlugin(['dist']), + new HtmlWebpackPlugin({ + title: 'Production' + }) + ], + output: { + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'dist') + } + }; 

webpack.dev.js

+ const merge = require('webpack-merge');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+ mode: 'development', + devtool: 'inline-source-map', + devServer: { + contentBase: './dist' + } + }); 

webpack.prod.js

+ const merge = require('webpack-merge');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+ mode: 'production', + });

在webpack.common.js中我們配置了input和output並引入了兩種環境都需要的外掛。在webpack.dev.s中我們把mode設定為了開發模式,同時添加了推薦的開發模式除錯工具(強大的 source mapping)以及簡單的devServer配置。最後在webpack.prod.js中我們把模式設定為了會使用UglifyJSPlugin的生產模式。

NPM指令碼:

現在我們需要根據新的配置修改一下啟動指令碼。我們將使用npm start作為開發環境指令碼,npm run build作為生產環境指令碼。

package.json

  {
    "name": "development",
    "version": "1.0.0",
    "description": "",
    "main": "src/index.js",
    "scripts": {
-     "start": "webpack-dev-server --open",
+     "start": "webpack-dev-server --open --config webpack.dev.js",
-     "build": "webpack"
+ "build": "webpack --config webpack.prod.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "clean-webpack-plugin": "^0.1.17", "css-loader": "^0.28.4", "csv-loader": "^2.1.1", "express": "^4.15.3", "file-loader": "^0.11.2", "html-webpack-plugin": "^2.29.0", "style-loader": "^0.18.2", "webpack": "^3.0.0", "webpack-dev-middleware": "^1.12.0", "webpack-dev-server": "^2.9.1", "webpack-merge": "^4.1.0", "xml-loader": "^1.2.1" } }

明確mode:

許多引用庫會根據process.env.NODE_ENV來決定包含什麼。在非生產模式下許多庫會新增一些額外的日誌和測試來使除錯更加簡單。而在process.env.NODE_ENV==="production"時它們可能會刪除或加入一部分重要的程式碼來提高真實使用者執行你的程式碼時的效能。webpack v4及以後明確mode屬性會自動幫你配置DefinePlugin外掛:

webpack.prod.js

  const merge = require('webpack-merge');
  const common = require('./webpack.common.js');

  module.exports = merge(common, {
    mode: 'production',
  });

ps:技術上講,NODE_ENV 是一個由 Node.js 暴露給執行指令碼的系統環境變數。通常用於決定在開發環境與生產環境(dev-vs-prod)下,伺服器工具、構建指令碼和客戶端 library 的行為。然而,與預期不同的是,無法在構建指令碼 webpack.config.js 中,將 process.env.NODE_ENV 設定為 "production"。因此,例如 process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js' 這樣的條件語句,在 webpack 配置檔案中,無法按照預期執行。

如果你使用了像react這樣的庫,那你新增完這個外掛後應該能發現輸出包的大小減小了非常多。而且本地src資料夾下的檔案都會識別這個變數,所以以下檢查會有效:

src/index.js

  import { cube } from './math.js';
+
+ if (process.env.NODE_ENV !== 'production') {
+   console.log('Looks like we are in development mode!');
+ }

  function component() {
    var element = document.createElement('pre');

    element.innerHTML = [
      'Hello webpack!',
      '5 cubed is equal to ' + cube(5)
    ].join('\n\n');

    return element;
  }

  document.body.appendChild(component());

壓縮:

注意,雖然UglifyJSPlugin 是程式碼壓縮方面比較好的選擇,但是還有一些其他可選擇項。以下有幾個同樣很受歡迎的外掛:

  • BabelMinifyWebpackPlugin
  • ClosureCompilerPlugin

如果決定嘗試以上這些,只要確保新外掛也會按照Tree Shaking指南中所陳述的,具有刪除未引用程式碼(dead code)的能力足矣。

Source Mapping:

我們鼓勵你在生產環境中啟用 source map,因為它們對除錯原始碼(debug)和執行基準測試(benchmark tests)很有幫助。也就是說,你應該選擇一個針對生產環境的構建速度較快的配置。對於本指南,我們將在生產環境中使用 source-map 選項,而不是我們在開發環境中用到的 inline-source-map

webpack.prod.js

  const merge = require('webpack-merge');
  const common = require('./webpack.common.js');

  module.exports = merge(common, {
    mode: 'production',
+   devtool: 'source-map'
  });

ps:避免在生產中使用 inline-*** 和 eval-***,因為它們可以增加 bundle 大小,並降低整體效能。

Split CSS:

正如在管理資源中最後的 載入CSS小節中所提到的,通常最好的做法是使用 ExtractTextPlugin 將 CSS 分離成單獨的檔案。在外掛文件中有一些很好的實現例子。disable 選項可以和 --env 標記結合使用,以允許在開發中進行內聯載入,推薦用於熱模組替換和構建速度。

CLI替代選項:

以上描述也可以通過命令列實現。例如,--optimize-minimize 標記將在後臺引用 UglifyJSPlugin。和以上描述的 DefinePlugin 例項相同,--define process.env.NODE_ENV="'production'" 也會做同樣的事情。並且,webpack -p 將自動地呼叫上述這些標記,從而呼叫需要引入的外掛。

這些簡便方式雖然都很不錯,但是我們通常建議只使用配置方式,因為在這兩種場景中下,配置方式能夠更好地幫助你瞭解自己正在做的事情。配置方式還可以讓你更方便地控制這兩個外掛中的其他選項。