分享:一篇webpack配置基礎絕好文章

weixin_34377065發表於2018-01-11

Webpack是目前基於React和Redux開發的應用的主要打包工具。我想使用Angular 2或其他框架開發的應用也有很多在使用Webpack。

當我第一次看到Webpack的配置檔案時,它看起來非常的陌生,我非常的疑惑。經過一段時間的嘗試之後我認為這是因為Webpack只是使用了比較特別的語法和引入了新的原理,因此會讓使用者感到疑惑。這些也是導致Webpack不被人熟悉的原因。

因為剛開始使用Webpack很讓人疑惑,我覺得有必要寫幾篇介紹Webpack的功能和特性的文章以幫助初學者快速理解。此文是最開始的一篇。

Webpack的核心原理

1. 一切皆模組

正如js檔案可以是一個“模組(module)”一樣,其他的(如css、image或html)檔案也可視作模 塊。因此,你可以require('myJSfile.js')亦可以require('myCSSfile.css')。這意味著我們可以將事物(業務)分割成更小的易於管理的片段,從而達到重複利用等的目的。

2. 按需載入

傳統的模組打包工具(module bundlers)最終將所有的模組編譯生成一個龐大的bundle.js檔案。但是在真實的app裡邊,“bundle.js”檔案可能有10M到15M之大可能會導致應用一直處於載入中狀態。因此Webpack使用許多特性來分割程式碼然後生成多個“bundle”檔案,而且非同步載入部分程式碼以實現按需載入。

好了,下面來看看那些令人困惑的部分吧。

1. 開發模式和生產模式

首先要知道的是Webpack有許許多多的特性,一些是”開發模式“下才有的,一些是”生產模式“下才有的,還有一些是兩種模式下都有的。

通常使用到Webpack如此多特性的專案都會有兩個比較大的Webpack配置檔案
複製程式碼

為了生成bundles檔案你可能在package.json檔案加入如下的scripts項:

"scripts": {
  // 執行npm run build 來編譯生成生產模式下的bundles
  "build": "webpack --config webpack.config.prod.js",
  // 執行npm run dev來生成開發模式下的bundles以及啟動本地server
  "dev": "webpack-dev-server"
 }
複製程式碼

2. webpack CLI 和webpack-dev-server

值得注意的是,Webpack作為模組打包工具,提供兩種使用者互動介面:

  • Webpack CLI tool:預設的互動方式(已隨Webpack本身安裝到本地)

  • webpack-dev-server:一個Node.js伺服器(需要開發者從npm自行安裝)

Webpack CLI(有利於生產模式下打包)

這種方式可以從命令列獲取引數也可以從配置檔案(預設叫webpack.config.js)獲取,將獲取到的引數傳入Webpack來打包

當然你也可以從命令列(CLI)開始學習Webpack,以後你可能主要在生產模式下使用到它。
複製程式碼

用法:

方式1: 
// 全域性模式安裝webpack
npm install webpack --g
// 在終端輸入
$ webpack // <--使用webpack.config.js生成bundle

方式 2 :
// 費全域性模式安裝webpack然後新增到package.json依賴裡邊
npm install webpack --save
// 新增build命令到package.json的scripts配置項
"scripts": {
 "build": "webpack --config webpack.config.prod.js -p",
 ...
 }
// 用法:
"npm run build"
複製程式碼

webpack-dev-server(有利於在開發模式下編譯)

這是一個基於Express.js框架開發的web server,預設監聽8080埠。server內部呼叫Webpack,這樣做的好處是提供了額外的功能如熱更新“Live Reload”以及熱替換“Hot Module Replacement”(即HMR) 用法:

方式 1:
// 全域性安裝
npm install webpack-dev-server --save
// 終端輸入
$ webpack-dev-server --inline --hot

用法 2:
// 新增到package.json scripts
"scripts": {
 "start": "webpack-dev-server --inline --hot",
 ...
 }
// 執行: 
$ npm start

// 瀏覽器預覽:
http://localhost:8080
複製程式碼

webpack VS Webpack-dev-server選項

注意像inline和hot這些選項是Webpack-dev-server特有的,而另外的如hide-modules則是CLI模式特有的選項。

webpack-dev-server CLI選項和配置項

另外值得注意的是你可以通過以下兩種方式向webpack-dev-server傳入引數:

  • 通過webpack.config.js檔案的"devServer"物件

  • 通過CLI選項

// 通過CLI傳參
webpack-dev-server --hot --inline
// 通過webpack.config.js傳參
devServer: {
  inline: true,
  hot:true
}
複製程式碼
我發現有時devServer配置項(hot: true 和inline: true)不生效,我更偏向使用如下的方式向CLI傳遞引數
// package.json
{
    "scripts": "webpack-dev-server --hot --inline"
}
注意:確定你沒有同時傳入hot:true和-hot
複製程式碼

webpack-dev-server的“hot” 和 “inline”選項

“inline”選項會為入口頁面新增“熱載入”功能,“hot”選項則開啟“熱替換(Hot Module Reloading)”,即嘗試重新載入元件改變的部分(而不是重新載入整個頁面)。如果兩個引數都傳入,當資源改變時,webpack-dev-server將會先嚐試HRM(即熱替換),如果失敗則重新載入整個入口頁面。

// 當資源發生改變,以下三種方式都會生成新的bundle,但是又有區別:
 
// 1. 不會重新整理瀏覽器
$ webpack-dev-server
//2. 重新整理瀏覽器
$ webpack-dev-server --inline
//3. 重新載入改變的部分,HRM失敗則重新整理頁面
$ webpack-dev-server  --inline --hot
複製程式碼

3. “entry”:值分別是字串、陣列和物件的情況

Enter配置項告訴Webpack應用的根模組或起始點在哪裡,它的值可以是字串、陣列或物件。這看起來可能令人困惑,因為不同型別的值有著不同的目的。

像絕大多數app一樣,倘若你的應用只有一個單一的入口,enter項的值你可以使用任意型別,最終輸出的結果都是一樣的。

enter:陣列型別

但是,如果你想新增多個彼此不互相依賴的檔案,你可以使用陣列格式的值。

例如,你可能在html檔案裡引用了“googleAnalytics.js”檔案,可以告訴Webpack將其加到bundle.js的最後。

用法:

//profile.html
<script src=”dist/profileEntry.js”></script>
//index.html
<script src=”dist/indexEntry.js”></script>
複製程式碼

注意:檔名取自“entry”物件的鍵名。

enter:混合型別

你也可以在enter物件裡使用陣列型別,例如下面的配置將會生成3個檔案:vender.js(包含三個檔案),index.js和profile.js檔案。

4. output:“path”項和“publicPath”項

output項告訴webpack怎樣儲存輸出結果以及儲存到哪裡。output的兩個配置項“path”和“publicPath”可能會造成困惑。

“path”僅僅告訴Webpack結果儲存在哪裡,然而“publicPath”項則被許多Webpack的外掛用於在生產模式下更新內嵌到css、html檔案裡的url值。

例如,在localhost(譯者注:即本地開發模式)裡的css檔案中邊你可能用“./test.png”這樣的url來載入圖片,但是在生產模式下“test.png”檔案可能會定位到CDN上並且你的Node.js伺服器可能是執行在HeroKu上邊的。這就意味著在生產環境你必須手動更新所有檔案裡的url為CDN的路徑。

然而你也可以使用Webpack的“publicPath”選項和一些外掛來在生產模式下編譯輸出檔案時自動更新這些url。

5. 模組載入和鏈式模組載入

模組載入器是可自由新增的Node模組,用於將不同型別的檔案“load”或“import”並轉換成瀏覽器可以識別的型別,如js、Stylesheet等。更高階的模組載入器甚至可以支援使用ES6裡邊的“require”或“import”引入模組。

例如,你可以使用babel-loader來將使用ES6語法寫成的檔案轉換成ES5:

module: {
 loaders: [{
  test: /\.js$/, // 匹配.js檔案,如果通過則使用下面的loader
  exclude: /node_modules/, // 排除node_modules資料夾
  loader: 'babel' // 使用babel(babel-loader的簡寫)作為loader
 }]
複製程式碼

鏈式(管道式)的載入器(從右往左執行)

多個loader可以用在同一個檔案上並且被鏈式呼叫。鏈式呼叫時從右到左執行且loader之間用“!”來分割。

例如,假設我們有一個名為“myCssFile.css”的css檔案,然後我們想將它的內容使用style標籤內聯到最終輸出的html裡邊。我們可以使用css-loader和style-loader兩個loader來達到目的。

module: {
 loaders: [{
  test: /\.css$/,
  loader: 'style!css' //(short for style-loader!css-loader)
 }]
複製程式碼

這裡展示它是如何工作的:

  1. Webpack在模組顳部搜尋在css的依賴項,即Webpack檢查js檔案是否有“require('myCssFile.css')”的引用,如果它發現有css的依賴,Webpack將css檔案交給“css-loader”去處理
  2. css-loader載入所有的css檔案以及css自身的依賴(如,@import 其他css)到JSON物件裡,Webpack然後將處理結果傳給“style-loader”
  3. style-loader接受JSON值然後新增一個style標籤並將其內嵌到html檔案裡

6. loader自身可以配置

模組載入器(loader)自身可以根據傳入不同的引數進行配置。

在下面的例子中,我們可以配置url-loader來將小於1024位元組的圖片使用DataUrl替換而大於1024位元組的圖片使用url,我們可以用如下兩種方式通過傳入“limit“引數來實現這一目的

7. babelrc 檔案

babal-loader使用”presets“配置項來標識如何將ES6語法轉成ES5以及如何轉換React的JSX成js檔案。我們可以用如下的方式使用”query“引數傳入配置:

module: {
  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel',
      query: {
        presets: ['react', 'es2015']
      }
    }
  ]
}
複製程式碼

8. 外掛

外掛一般都是用於輸出bundle的node模組。

例如,uglifyJSPlugin獲取bundle.js然後壓縮和混淆內容以減小檔案體積。

類似的extract-text-webpack-plugin內部使用css-loader和style-loader來收集所有的css到一個地方最終將結果提取結果到一個獨立的”styles.css“檔案,並且在html裡邊引用style.css檔案。

//webpack.config.js
// 獲取所有的.css檔案,合併它們的內容然後提取css內容到一個獨立的”styles.css“裡
var ETP = require("extract-text-webpack-plugin");

module: {
 loaders: [
  {test: /\.css$/, loader:ETP.extract("style-loader","css-loader") }
  ]
},
plugins: [
    new ExtractTextPlugin("styles.css") //Extract to styles.css file
  ]
}
複製程式碼

注意:如果你只是想把css使用style標籤內聯到html裡,你不必使用extract-text-webpack-plugin,僅僅使用css loader和style loader即可

9. 載入器(loader)和外掛

你可能已經意識到了,Loader處理單獨的檔案級別並且通常作用於包生成之前或生成的過程中。

而外掛則是處理包(bundle)或者chunk級別,且通常是bundle生成的最後階段。一些外掛如commonschunkplugin甚至更直接修改bundle的生成方式。

10. 處理檔案的副檔名

很多Webpack的配置檔案都有一個resolve屬性,然後就像下面程式碼所示有一個空字串的值。空字串在此是為了resolve一些在import檔案時不帶副檔名的表示式,如require('./myJSFile')或者import myJSFile from './myJSFile'(譯者注:實際就是自動新增字尾,預設是當成js檔案來查詢路徑)

{
 resolve: {
   extensions: ['', '.js', '.jsx']
 }
}
複製程式碼

就這麼多。

轉載自:https://segmentfault.com/a/1190000005089993?utm_source=tuicool&utm_medium=referral

相關文章