webpack

WebPack 5分鐘入門

webpack是一個現代JavaScript應用程式的靜態模組打包工具。當webpack處理應用程式時,它會在...

>

webpack 是一個現代 JavaScript 應用程式的靜態模組打包工具。當 webpack 處理應用程式時,它會在內部構建一個 依賴圖(dependency graph),此依賴圖會對映專案所需的每個模組,並生成一個或多個 bundle。

快速入門

https://webpack.js.org/guides/getting-started/

安裝 Webpack

雖然webpack可以全域性安裝(npm install webpack -g),但是最好將它設定為專案的依賴來避免問題。這種方式對持續整合是比較友好的。一個CI系統可以安裝你的本地依賴,用它們來編譯你的專案,然後push結果給一個伺服器。

給專案增加webpack依賴:

npm install webpack --save-dev # -D if you want to save typing

##執行 Webpack

你可以使用npm bin來顯示可執行檔案的準確路徑。大部分指向如 ./node_modules/.bin這樣的路徑。 嘗試執行 在命令列執行node_modules/.bin/webpack 或類似的命令。

Setting Up Webpack Configuration

我們開始使用第一個外掛html-webpack-plugin. HtmlWebpackPlugin 為應用生成一個 index.html 並新增一個 script 來載入生成的bundle。

npm install html-webpack-plugin --save-dev

最最小情況下,你的配置檔案至少有entryoutput欄位。 通常你會看到更多,因為i哦你要指定webpack怎樣處理不同檔案型別並解析它們。

Entry告訴webpack從哪裏開始解析應用。在多頁面的應用中,你每個頁面都擁有一個entry。或者你可以每個entry都一個配置檔案。

所有output相關的路徑你可以在 output.path 欄位看到。如果你擁有一個output關係選項,且寫 styles/[name].css, 這將會被擴充套件,以便你獲取 <output.path> + <specific path>. 例如: ~/webpack-demo/build/styles/main.css.

爲了展示怎樣用 HtmlWebpackPlugin來連線entryoutput , 考慮以下程式碼:

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const PATHS = {
  src: path.join(__dirname, "src"),
  dist: path.join(__dirname, "dist"),
};

module.exports = {
  entry: {
    dist: PATHS.src,
  },
  output: {
    path: PATHS.dist,
    filename: "[name].js",
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "Webpack demo",
    }),
  ],
};

entry 可以使用context來設定一個相對路徑。然而, 大量地方期待絕對路徑而不是相對路徑,這樣可以避免混亂。

T> [name] 是個佔位符. 在這個例子中 [name] 會被替換為entry的名稱 ‘dist’.

如果執行 node_modules/.bin/webpack,會看到輸出

Hash: eb614669e5d781eaa5ed
Version: webpack 3.10.0
Time: 659ms
     Asset       Size  Chunks                    Chunk Names
   dist.js     544 kB       0  [emitted]  [big]  dist
index.html  181 bytes          [emitted]
   [0] ./src/index.js 242 bytes {0} [built]
   [2] (webpack)/buildin/global.js 509 bytes {0} [built]
   [3] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
       [2] (webpack)/buildin/global.js 509 bytes {0} [built]
       [3] (webpack)/buildin/module.js 517 bytes {0} [built]
        + 2 hidden moduless

這些控制檯的輸出有:

  • Hash: eb614669e5d781eaa5ed - 你可以通過該[hash] 佔位符,讓客戶端js失效。
  • Version: webpack 3.10.0 - Webpack版本號.
  • Time: 659ms - 執行構建的時間
  • dist.js 544 kB 0 [emitted] [big] dist - 產生的asset的名稱, 大小, 它相關的 chunks 的id, 產生的狀態, chunk的名稱.
  • index.html 180 bytes [emitted] - 另一個生成的asset
  • [0] ./src/index.js 242 bytes {0} [built] - entry asset的id, 名稱, 大小, entry chunk ID, 生成的方式
  • Child html-webpack-plugin for "index.html": - 外掛相關的輸出,

在目錄dist/下,. 通過瀏覽器開啟 dist/index.html

T> 如果你想要webpack在第一個錯誤的時候就停止執行, 設定 bail: true 。 該設定會殺掉整個webpack程序。該行為在你構建CI環境時候會很有用。

T> 除了一個配置物件, webpack 接受一個配置的陣列。 你可以返回Promise 和最終的 resolve 給一個配置。

增加個構建的快捷方式

將構建命令存放到package.json script中

package.json

"scripts": {
  "build": "webpack"
},

執行npm run build

這個之所以生效,是因為npm臨時將node_modules/.bin增加到path上。所以你可以不用寫 "build": "node_modules/.bin/webpack"而是 "build": "webpack"。你可以執行npm run命令來檢視可以有哪些命令可以使用。

T> 其他的如 npm startnpm test。 這些常用的可以不使用npm run執行,甚至可以用npm t來執行npm test

HtmlWebpackPlugin 擴充套件

雖然你可以替換自己的HtmlWebpackPlugin模板, 但是有已有的模板可使用 html-webpack-template 或者 html-webpack-template-pug.

還有其他專門的擴充套件HtmlWebpackPlugin功能的外掛:

webpack-dev-server

LiveReload 或者 Browsersync這樣的工具, 可以在你開發應用的時候重新整理瀏覽器,且避免因css變更造成的重新整理。可以通過browser-sync-webpack-plugin讓Browsersync和webpack一起工作,但webpack本身有更多的技能可以使用。

Webpack watch 模式和 webpack-dev-server

一個更好的開發環境的第一步是在watch模式下使用webpack。你可以通過webpack --watch來使用該模式。一旦被啟用,它將檢測你的檔案的變更,並自動地重新編譯。 webpack-dev-server (WDS) 實現了自己的監控模式,且更進一步。

WDS是個執行在記憶體中的開發伺服器,意味著bundle內容不會寫入到檔案中,而是記憶體中。 當你除錯程式碼和樣式的時候,這是個重要的區別。

預設情況下WDS在你開發自己的應用的時候會自動在瀏覽器端重新整理內容,你不用手工重新整理。然而它也支援一個高階的webpack特性, Hot Module Replacement (HMR)。

HMR允許你增量地更新瀏覽器載入的檔案。

WDS提供了快速處理增量檔案的介面。然而爲了讓這個工作高效,你不得不為客戶端程式碼實現該介面。這個對如css這樣的東西是無所謂的,因為它們是無狀態的,但是在處理javascript框架或者庫的時候就會碰到問題了。

WDS生成檔案

雖然WDS預設在記憶體裡操作可以提高效能,但是有時候可能需要生成檔案到檔案系統中。這個在你和其他需要使用這些檔案的伺服器做整合的時候,就會特別有用。 webpack-disk-plugin, write-file-webpack-plugin, 以及 html-webpack-harddisk-plugin 可以完成這項工作。

W> 你們應該只在開發階段使用WDS。在生產上,你可以考慮其他的標準方案,如Apache或者Nginx。

WDS入門

安裝

npm install webpack-dev-server --save-dev

會在npm bin目錄下生成對應的可執行檔案, 你可以執行對應目錄下的webpack-dev-server

webpack-demo $ ./node_modules/.bin/webpack-dev-server

在執行WDS之後,你就有個開發伺服器執行在 http://localhost:8080。瀏覽器自動重新整理就緒了,雖然還是很基礎的級別。

在專案中加上WDS

爲了在專案中整合WDS,在npm scripts中定義啟動指令碼。爲了遵循npm約定,呼叫start

package.json

"scripts": {
  "start": "webpack-dev-server",
  "build": "webpack"
},

T> WDS 會獲取配置,如webpack自身的配置。 應用相同的規則。

此時開啟瀏覽器http://localhost:8080/,你就可以看到Hello webpack

如果你嘗試修改程式碼,你會看到瀏覽器進行了同步重新整理。

如果預設埠被佔用了,WDS會嘗試其他埠。終端輸出的內容會說明最終使用的埠是哪個。

通過Webpack配置檔案來配置WDS

爲了定製WDS的功能,需要在webpack配置檔案中定義devServer欄位。當然也可以在命令列設定這些選項,但是通過webpack來管理配置會高效點。

增加附加的功能,如下:

webpack.config.js

...

module.exports = {
  ...
  devServer: {
    // Display only errors to reduce the amount of output.
    stats: "errors-only",

    // Parse host and port from env to allow customization.
    //
    // If you use Docker, Vagrant or Cloud9, set
    // host: options.host || "0.0.0.0";
    //
    // 0.0.0.0 is available to all network devices
    // unlike default `localhost`.
    host: process.env.HOST, // Defaults to `localhost`
    port: process.env.PORT, // Defaults to 8080
  },
};

在這些改變之後,你可以通過環境變數配置server的host和port選項(如: PORT=3000 npm start).

如果你通過http://localhost:8080/webpack-dev-server/訪問伺服器, WDS在頂部提供了狀態。

T> dotenv 可以通過一個環境變數檔案 .env 來定義環境變數. dotenv 可以讓你快速地處理host和port的配置。

T> 如果你使用了HTML5基於路由的History API, 可以配置devServer.historyApiFallback

Enabling Error Overlay

WDS提供了一個在瀏覽器端檢視編譯的告警和錯誤資訊的overlay

webpack.config.js

module.exports = {
  devServer: {
    ...
    // overlay: true is equivalent
    overlay: {
      errors: true,
      warnings: true,
    },
  },
};

執行npm start啟動服務,可以在瀏覽器上檢視overlay效果,—TODO 沒出現呀

Enabling Hot Module Replacement

HMR是將webpack進行分離的特性。實現它需要服務端和客戶端都做附加處理。TODO-HMR專題

從網路訪問開發伺服器

可以通過環境變數自定義host和port的配置。配置好對應的host資訊即可。

Making It Faster to Develop Configuration

WDS在你修改一個打包了的檔案的時候會重啟伺服器,但是在你編輯webpack配置檔案的時候呢?任何修改都人工重啟開發伺服器會變得讓人厭煩。 修改webpack服務的動作可以如 discussed in GitHub 中討論的一樣,採用 nodemon 監控工具來自動化。

首先你需要安裝nodemon

npm install nodemon --save-dev

然後配置npm script來使用它 package.json

"scripts": {
  "start": "nodemon --watch webpack.config.js --exec \"webpack-dev-server\"",
  "build": "webpack"
},

輪詢來替代監控檔案

有時候你的系統可能無法使用WDS的檔案監控功能。可以使用輪詢來替代。

webpack.config.js

module.exports = {
  ...
  devServer: {
    watchOptions: {
      aggregateTimeout: 300,

      poll: 1000,
    },
  },
  plugins: [
    // 忽略node_modules,否則cpu使用率將影響會比較大
    new webpack.WatchIgnorePlugin([
      path.join(__dirname, "node_modules")
    ]),
  ],
};

其他使用 webpack-dev-server 的方式

你可以終端傳入WDS選項。但是放在配置檔案裏面會更清晰點。

另外,如果你使用Express server和中介軟體,有一些其他選項可使用:

如果你想控制更多,也有Node.js API

W> CLI和Node API有 輕微的不同 .

其他 webpack-dev-server 的特性

WDS提供了以上的功能,也有一些其他的欄位可能需要關注下:

  • devServer.contentBase - 假設你不需要動態地生成index.html檔案,且更喜歡在自己指定的目錄進行維護,你需要對WDS顯式指定。contentBase 接受一個path(如 "build")或者一個paths陣列(如:["build", "images"]),這些預設是專案的root。
  • devServer.proxy - 如果你使用多個伺服器,你需要代理WDS請求,proxy設定代理對映的路徑 (如 { "/api": "http://localhost:3000/api" }) ,會將對應的請求指向對應的伺服器。預設情況下,代理服務不生效。
  • devServer.headers - 對你的請求自定義請求頭.

T> 更多資訊見官方文件 .

開發期間的外掛

webpack有許多在開發階段很有用的外掛。

Output 外掛

也有讓webpack輸出更容易被人理解的外掛:

  • system-bell-webpack-plugin 在構建失敗的時候發出系統bell聲響,而不是無聲的。
  • webpack-notifier 使用系統的通知機制來讓你知道webpack狀態。
  • nyan-progress-webpack-plugin 用來在構建階段獲取更乾淨的輸出。如果你使用如Travis這樣的CI工具。Webpack的ProgressPlugin提供的同樣的功能。
  • friendly-errors-webpack-plugin 提高webpack的錯誤報告。它獲取通常的錯誤,並以一種友好的方式展現出來。
  • webpack-dashboard 對標準的webpack輸出給你提供了一個基於終端的dashboard。如果你更喜歡視覺化的輸出,這個可能會更好點。

組合配置

開發和生產使用各自單獨的配置檔案更容易理解。並移除任何可重用部分。隨著你的專案演進,你不得不找出更高效地管理webpack配置的方式。

管理配置檔案的可選方法

你可以通過以下方式來管理webpack配置檔案。

  • 對每個環境都維護單獨的配置檔案,使用的時候用--config引數明確指定,共享的配置通過模組匯入。你可以在 webpack/react-starter專案中看到這種用法
  • 將配置推送到一個庫中,然後你使用該庫。如hjs-webpack, Neutrino, webpack-blocks.
  • 推送配置給一個工具, 如 create-react-app, kyt, nwb.
  • 在一個單獨的檔案內維護所有的配置,並對它進行條件分支處理,依賴於--env引數。

這些方法可以被組合使用來建立一個更高階別的配置,來將更小的部分組合起來。這些部分可以被加到一個庫中,然後通過npm來讓多個專案來使用相同的配置。

通過合併的方式來編寫配置

如果配置檔案被拆成多個單獨檔案,他們都需要被重新組合。通常這就意味著合併物件和陣列。 webpack-merge 被開發出來,用來消除 使用 Object.assignArray.concat的問題。

webpack-merge 做了兩件事情:它合併了陣列,併合並了物件,而不是覆蓋了配置。以下例子可以說明功能

> merge = require("webpack-merge")
...
> merge(
... { a: [1], b: 5, c: 20 },
... { a: [2], b: 10, d: 421 }
... )
{ a: [ 1, 2 ], b: 10, c: 20, d: 421 }

webpack-merge 設定提供了合併策略的欄位,策略允許你強行append,prepend,或者replace內容。它被認為是非常有用的工具。你可以認為它是個值得學習的工具,並在你的工作中合適的地方使用它。

T> webpack-chain 提供了一個流式API來配置webpack。

開始使用 webpack-merge

安裝

npm install webpack-merge --save-dev

爲了獲取更高層次的抽象,你可以定義 webpack.config.js為更高層次的配置,並將webpack.parts.js作為它的部分配置,匯入。

這裏有從已有程式碼中抽取出來的小功能。

webpack.parts.js

exports.devServer = ({ host, port } = {}) => ({
  devServer: {
    stats: "errors-only",
    host, // Defaults to `localhost`
    port, // Defaults to 8080
    overlay: {
      errors: true,
      warnings: true,
    },
  },
});

T> 相同的 stats 對生產的配置同樣有效。可以看 官方文件 查詢所有選項。

爲了使用該配置檔案片段,如下方式修改 webpack.config.js

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const merge = require("webpack-merge");

const parts = require("./webpack.parts");

const PATHS = {
  app: path.join(__dirname, "app"),
  build: path.join(__dirname, "build"),
};

const commonConfig = merge([
  {
    entry: {
      app: PATHS.app,
    },
    output: {
      path: PATHS.build,
      filename: "[name].js",
    },
    plugins: [
      new HtmlWebpackPlugin({
        title: "Webpack demo",
      }),
    ],
  },
]);

const productionConfig = merge([]);

const developmentConfig = merge([
  parts.devServer({
    // Customize host/port here if needed
    host: process.env.HOST,
    port: process.env.PORT,
  }),
]);

module.exports = env => {
  if (env === "production") {
    return merge(commonConfig, productionConfig);
  }

  return merge(commonConfig, developmentConfig);
};

這裏不直接返回配置,而是獲取引數env來返回對應的配置,這個指定的引數webpack是支援的。爲了使用它,package.json需要做如下調整。

package.json

"scripts": {
  "start": "webpack-dev-server --env development",
  "build": "webpack --env production"
},

現在你不用擔心怎樣合併不同配置了。

理解 --env

雖然 --env 可以讓你給配置傳個字串, 實際上它的能力不止這些。考慮下以下例子:

package.json

"scripts": {
  "start": "webpack-dev-server --env development",
  "build": "webpack --env.target production"
},

除了字串,你還可以用物件做為配置 { target: "production" } ,引數都可以儲存到 env 物件上。 如果你同時配置了字串和物件,如--env foo--env.target 那麼會使用字串。 Webpack依賴於 yargs 進行解析.

組合配置的好處

拆分配置最大的好處是可以對不同環境的配置抽取公共的部分。可以將配置根據功能最小化拆分,然後在不同專案進行重用。

現在你可以將配置當作個依賴,在不同專案之間進行重用,減少多個專案之間重複的類似配置。

每個方法都有自身的優缺點。基於組合的方式是個好的開始。組合最大問題可能是你需要知道你正在做什麼,可能一開始你並不會採取組合的方式。

你通常可以遍歷介面來查詢更好的,通過傳遞個物件,而非多個引數,你可以改變每個組成部分的行為,而不影響它本身的api。你可以在你需要的時候將API開放出去。

配置檔案的佈局

拆分配置檔案的目錄結構

每個目標拆個檔案

如果每個target都拆個配置檔案,最終目錄結構如下:

.
└── config
    ├── webpack.common.js
    ├── webpack.development.js
    ├── webpack.parts.js
    └── webpack.production.js

在這種情況下,你可以通過webpack --config 引數和 merge 函式來通過 module.exports = merge(common, config);合併配置.

每種用途的配置拆個檔案

爲了增加配置能力,可以將webpack.parts.js按用途拆成不同檔案。

.
└── config
    ├── parts
    │   ├── devserver.js
    ...
    │   ├── index.js
    │   └── javascript.js
    └── ...

這樣可以根據分類快速找到對應的配置。當然,也可以將不同功能的配置放在一個檔案中,並用註釋將它們分隔開來。

Pushing Parts to Packages

假設所有配置都是js,那麼你把它當作一個包或者多個包處理都是一樣的。可以將共享的配置打包,以便你可以跨多個專案使用它。

Facebook Profile photo
Written by Nat
This is the author box. A short description about the author of this article. Could be their website link, what they like to read about and so on. Profile