webpack配置

webpack配置

查看全部文檔頁面: 全棧開發,獲取更多信息。

原文連接:第2章 配置,原文廣告模態框遮擋,閱讀體驗很差,因此整理成本文,方便查找。css

配置 Webpack 的方式有兩種:html

  1. 經過一個 JavaScript 文件描述配置,例如使用 webpack.config.js 文件裏的配置;
  2. 執行 Webpack 可執行文件時經過命令行參數傳入,例如 webpack --devtool source-map

這兩種方式能夠相互搭配,例如執行 Webpack 時經過命令 webpack --config webpack-dev.config.js 指定配置文件,再去 webpack-dev.config.js 文件裏描述部分配置。node

按照配置所影響的功能來劃分,可分爲:react

  • Entry 配置模塊的入口;
  • Output 配置如何輸出最終想要的代碼;
  • Module 配置處理模塊的規則;
  • Resolve 配置尋找模塊的規則;
  • Plugins 配置擴展插件;
  • DevServer 配置 DevServer;
  • 其它配置項 其它零散的配置項;
  • 總體配置結構 總體地描述各配置項的結構;
  • 多種配置類型 配置文件不止能夠返回一個 Object,還有其餘返回形式;
  • 配置總結 尋找配置 Webpack 的規律,減小思惟負擔。

Entry

Webpack 在尋找相對路徑的文件時會以 context 爲根目錄,context 默認爲執行啓動 Webpack 時所在的當前工做目錄。jquery

若是想改變 context 的默認配置,能夠在配置文件裏設置:webpack

module.exports = {
  context: path.resolve(__dirname, 'app')
}

注意, context 必須是一個絕對路徑的字符串。 除此以外,還能夠經過在啓動 Webpack 時帶上參數 webpack --context 來設置 context。web

Chunk 名稱

Webpack 會爲每一個生成的 Chunk 取一個名稱,Chunk 的名稱和 Entry 的配置有關:正則表達式

  • 若是 entry 是一個 stringarray,就只會生成一個 Chunk,這時 Chunk 的名稱是 main
  • 若是 entry 是一個 object,就可能會出現多個 Chunk,這時 Chunk 的名稱是 object 鍵值對裏鍵的名稱。

配置動態 Entry

假如項目裏有多個頁面須要爲每一個頁面的入口配置一個 Entry ,但這些頁面的數量可能會不斷增加,則這時 Entry 的配置會受到到其餘因素的影響致使不能寫成靜態的值。其解決方法是把 Entry 設置成一個函數去動態返回上面所說的配置,代碼以下:npm

// 同步函數
entry: () => {
  return {
    a:'./pages/a',
    b:'./pages/b',
  }
};
// 異步函數
entry: () => {
  return new Promise((resolve)=>{
    resolve({
       a:'./pages/a',
       b:'./pages/b',
    });
  });
};

Output

output 配置如何輸出最終想要的代碼。output 是一個 object,裏面包含一系列配置項:json

filename

output.filename 配置輸出文件的名稱,爲 string 類型。 若是隻有一個輸出文件,則能夠把它寫成靜態不變的:

filename: 'bundle.js'

可是在有多個 Chunk 要輸出時,就須要藉助模版和變量了。前面說到 Webpack 會爲每一個 Chunk取一個名稱,能夠根據 Chunk 的名稱來區分輸出的文件名:

filename: '[name].js'

代碼裏的 [name] 表明用內置的 name 變量去替換[name],這時你能夠把它看做一個字符串模塊函數, 每一個要輸出的 Chunk 都會經過這個函數去拼接出輸出的文件名稱。

變量名 含義
id Chunk 的惟一標識,從0開始
name Chunk 的名稱
hash Chunk 的惟一標識的 Hash 值
chunkhash Chunk 內容的 Hash 值

其中 hashchunkhash 的長度是可指定的,[hash:8] 表明取8位 Hash 值,默認是20位。

注意 ExtractTextWebpackPlugin 插件是使用 contenthash 來表明哈希值而不是 chunkhash, 緣由在於 ExtractTextWebpackPlugin 提取出來的內容是代碼內容自己而不是由一組模塊組成的 Chunk。

chunkFilename

output.chunkFilename 配置無入口的 Chunk 在輸出時的文件名稱。 chunkFilename 和上面的 filename 很是相似,但 chunkFilename 只用於指定在運行過程當中生成的 Chunk 在輸出時的文件名稱。 常見的會在運行時生成 Chunk 場景有在使用 CommonChunkPlugin、使用 import('path/to/module') 動態加載等時。 chunkFilename 支持和 filename 一致的內置變量。

path

output.path 配置輸出文件存放在本地的目錄,必須是 string 類型的絕對路徑。一般經過 Node.js 的 path 模塊去獲取絕對路徑:

path: path.resolve(__dirname, 'dist_[hash]')

publicPath

在複雜的項目裏可能會有一些構建出的資源須要異步加載,加載這些異步資源須要對應的 URL 地址。

output.publicPath 配置發佈到線上資源的 URL 前綴,爲string 類型。 默認值是空字符串 '',即便用相對路徑。

把構建出的資源文件上傳到 CDN 服務上,以利於加快頁面的打開速度。配置代碼以下:

filename:'[name]_[chunkhash:8].js'
publicPath: 'https://cdn.example.com/assets/'

這時發佈到線上的 HTML 在引入 JavaScript 文件時就須要:

<script src='https://cdn.example.com/assets/a_12345678.js'></script>

使用該配置項時要當心,稍有不慎將致使資源加載404錯誤。

output.pathoutput.publicPath 都支持字符串模版,內置變量只有一個:hash 表明一次編譯操做的 Hash 值。

crossOriginLoading

Webpack 輸出的部分代碼塊可能須要異步加載,而異步加載是經過 JSONP 方式實現的。 JSONP 的原理是動態地向 HTML 中插入一個 <script src="url"></script> 標籤去加載異步資源。

output.crossOriginLoading 則是用於配置這個異步插入的標籤的 crossorigin 值。

script 標籤的 crossorigin 屬性能夠取如下值:

  • false(默認) 在加載此腳本資源時不會帶上用戶的 Cookies;
  • use-credentials 在加載此腳本資源時會帶上用戶的 Cookies。

一般用設置 crossorigin 來獲取異步加載的腳本執行時的詳細錯誤信息。

libraryTarget 和 library

當用 Webpack 去構建一個能夠被其餘模塊導入使用的庫時須要用到它們。

  • output.libraryTarget 配置以何種方式導出庫。
  • output.library 配置導出庫的名稱。

假如配置了 output.library='LibraryName',則輸出和使用的代碼以下:

// Webpack 輸出的代碼
var LibraryName = lib_code;

// 使用庫的方法
LibraryName.doSomething();

假如 output.library 爲空,則將直接輸出:lib_code

其中 lib_code 代指導出庫的代碼內容,是有返回值的一個自執行函數。

它們一般搭配在一塊兒使用。

output.libraryTarget 是字符串的枚舉類型,支持如下配置。

var (默認)

編寫的庫將經過 var 被賦值給經過 library 指定名稱的變量。

commonjs

編寫的庫將經過 CommonJS2 規範導出,輸出和使用的代碼以下:

// Webpack 輸出的代碼
module.exports = lib_code;

// 使用庫的方法
require('library-name-in-npm').doSomething();
CommonJS2 和 CommonJS 規範很類似,差異在於 CommonJS 只能用 exports 導出,而 CommonJS2 在 CommonJS 的基礎上增長了 module.exports 的導出方式。

output.libraryTarget 爲 commonjs2 時,配置 output.library 將沒有意義。

this

編寫的庫將經過 this 被賦值給經過 library 指定的名稱,輸出和使用的代碼以下:

// Webpack 輸出的代碼
this['LibraryName'] = lib_code;

// 使用庫的方法
this.LibraryName.doSomething();

window

編寫的庫將經過 window 被賦值給經過 library 指定的名稱,即把庫掛載到 window 上,輸出和使用的代碼以下:

// Webpack 輸出的代碼
window['LibraryName'] = lib_code;

// 使用庫的方法
window.LibraryName.doSomething();

global

編寫的庫將經過 global 被賦值給經過 library 指定的名稱,即把庫掛載到 global 上,輸出和使用的代碼以下:

// Webpack 輸出的代碼
global['LibraryName'] = lib_code;

// 使用庫的方法
global.LibraryName.doSomething();

libraryExport

output.libraryExport 配置要導出的模塊中哪些子模塊須要被導出。 它只有在 output.libraryTarget 被設置成 commonjs 或者 commonjs2 時使用纔有意義。

假如要導出的模塊源代碼是:

export const a=1;
export default b=2;

如今想讓構建輸出的代碼只導出其中的 a,能夠把 output.libraryExport 設置成 a,那麼構建輸出的代碼和使用方法將變成以下:

// Webpack 輸出的代碼
module.exports = lib_code['a'];

// 使用庫的方法
require('library-name-in-npm')===1;

Module

配置 Loader

rules 配置模塊的讀取和解析規則,一般用來配置 Loader。其類型是一個數組,數組裏每一項都描述瞭如何去處理部分文件。 配置一項 rules 時大體經過如下方式:

  1. 條件匹配:經過 testincludeexclude 三個配置項來命中 Loader 要應用規則的文件。
  2. 應用規則:對選中後的文件經過 use 配置項來應用 Loader,能夠只應用一個 Loader 或者按照從後往前的順序應用一組 Loader,同時還能夠分別給 Loader 傳入參數。
  3. 重置順序:一組 Loader 的執行順序默認是從右到左執行,經過 enforce 選項可讓其中一個 Loader 的執行順序放到最前或者最後。
module: {
  rules: [
    {
      // 命中 JavaScript 文件
      test: /\.js$/,
      // 用 babel-loader 轉換 JavaScript 文件
      // ?cacheDirectory 表示傳給 babel-loader 的參數,用於緩存 babel 編譯結果加快從新編譯速度
      use: ['babel-loader?cacheDirectory'],
      // 只命中src目錄裏的js文件,加快 Webpack 搜索速度
      include: path.resolve(__dirname, 'src')
    },
    {
      // 命中 SCSS 文件
      test: /\.scss$/,
      // 使用一組 Loader 去處理 SCSS 文件。
      // 處理順序爲從後到前,即先交給 sass-loader 處理,再把結果交給 css-loader 最後再給 style-loader。
      use: ['style-loader', 'css-loader', 'sass-loader'],
      // 排除 node_modules 目錄下的文件
      exclude: path.resolve(__dirname, 'node_modules'),
    },
    {
      // 對非文本文件採用 file-loader 加載
      test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
      use: ['file-loader'],
    },
  ]
}

在 Loader 須要傳入不少參數時,你還能夠經過一個 Object 來描述,例如在上面的 babel-loader 配置中有以下代碼:

use: [
  {
    loader:'babel-loader',
    options:{
      cacheDirectory:true,
    },
    // enforce:'post' 的含義是把該 Loader 的執行順序放到最後
    // enforce 的值還能夠是 pre,表明把 Loader 的執行順序放到最前面
    enforce:'post'
  },
  // 省略其它 Loader
]

上面的例子中 test include exclude 這三個命中文件的配置項只傳入了一個字符串或正則,其實它們還都支持數組類型,使用以下:

{
  test:[
    /\.jsx?$/,
    /\.tsx?$/
  ],
  include:[
    path.resolve(__dirname, 'src'),
    path.resolve(__dirname, 'tests'),
  ],
  exclude:[
    path.resolve(__dirname, 'node_modules'),
    path.resolve(__dirname, 'bower_modules'),
  ]
}

數組裏的每項之間是的關係,即文件路徑符合數組中的任何一個條件就會被命中。

noParse

noParse 配置項可讓 Webpack 忽略對部分沒采用模塊化的文件的遞歸解析和處理,這樣作的好處是能提升構建性能。 緣由是一些庫例如 jQuery 、ChartJS 它們龐大又沒有采用模塊化標準,讓 Webpack 去解析這些文件耗時又沒有意義。

noParse 是可選配置項,類型須要是 RegExp[RegExp]function 其中一個。

例如想要忽略掉 jQuery 、ChartJS,可使用以下代碼:

// 使用正則表達式
noParse: /jquery|chartjs/

// 使用函數,從 Webpack 3.0.0 開始支持
noParse: (content)=> {
  // content 表明一個模塊的文件路徑
  // 返回 true or false
  return /jquery|chartjs/.test(content);
}
注意被忽略掉的文件裏不該該包含 importrequiredefine 等模塊化語句,否則會致使構建出的代碼中包含沒法在瀏覽器環境下執行的模塊化語句。

parser

由於 Webpack 是以模塊化的 JavaScript 文件爲入口,因此內置了對模塊化 JavaScript 的解析功能,支持 AMDCommonJSSystemJSES6

parser 屬性能夠更細粒度的配置哪些模塊語法要解析哪些不解析,和 noParse 配置項的區別在於 parser 能夠精確到語法層面, 而 noParse 只能控制哪些文件不被解析。 parser 使用以下:

module: {
  rules: [
    {
      test: /\.js$/,
      use: ['babel-loader'],
      parser: {
      amd: false, // 禁用 AMD
      commonjs: false, // 禁用 CommonJS
      system: false, // 禁用 SystemJS
      harmony: false, // 禁用 ES6 import/export
      requireInclude: false, // 禁用 require.include
      requireEnsure: false, // 禁用 require.ensure
      requireContext: false, // 禁用 require.context
      browserify: false, // 禁用 browserify
      requireJs: false, // 禁用 requirejs
      }
    },
  ]
}

Resolve

Webpack 在啓動後會從配置的入口模塊出發找出全部依賴的模塊,Resolve 配置 Webpack 如何尋找模塊所對應的文件。 Webpack 內置 JavaScript 模塊化語法解析功能,默認會採用模塊化標準里約定好的規則去尋找,但你也能夠根據本身的須要修改默認的規則。

alias

resolve.alias 配置項經過別名來把原導入路徑映射成一個新的導入路徑。例如使用如下配置:

// Webpack alias 配置
resolve:{
  alias:{
    components: './src/components/'
  }
}

當你經過 import Button from 'components/button' 導入時,實際上被 alias 等價替換成了 import Button from './src/components/button'

以上 alias 配置的含義是把導入語句裏的 components 關鍵字替換成 ./src/components/

這樣作可能會命中太多的導入語句,alias 還支持 $ 符號來縮小範圍到只命中以關鍵字結尾的導入語句:

resolve:{
  alias:{
    'react$': '/path/to/react.min.js'
  }
}

react$ 只會命中以 react 結尾的導入語句,即只會把 import 'react' 關鍵字替換成 import '/path/to/react.min.js'

mainFields

有一些第三方模塊會針對不一樣環境提供幾分代碼。 例如分別提供採用 ES5 和 ES6 的2份代碼,這2份代碼的位置寫在 package.json 文件裏,以下:

{
  "jsnext:main": "es/index.js",// 採用 ES6 語法的代碼入口文件
  "main": "lib/index.js" // 採用 ES5 語法的代碼入口文件
}

Webpack 會根據 mainFields 的配置去決定優先採用哪份代碼,mainFields 默認以下:

mainFields: ['browser', 'main']

Webpack 會按照數組裏的順序去 package.json 文件裏尋找,只會使用找到的第一個。

假如你想優先採用 ES6 的那份代碼,能夠這樣配置:

mainFields: ['jsnext:main', 'browser', 'main']

extensions

在導入語句沒帶文件後綴時,Webpack 會自動帶上後綴後去嘗試訪問文件是否存在。 resolve.extensions 用於配置在嘗試過程當中用到的後綴列表,默認是:

extensions: ['.js', '.json']

modules

resolve.modules 配置 Webpack 去哪些目錄下尋找第三方模塊,默認是隻會去 node_modules 目錄下尋找。

有時你的項目裏會有一些模塊會大量被其它模塊依賴和導入,因爲其它模塊的位置分佈不定,針對不一樣的文件都要去計算被導入模塊文件的相對路徑, 這個路徑有時候會很長,就像這樣 import '../../../components/button' 這時你能夠利用 modules 配置項優化,假如那些被大量導入的模塊都在 ./src/components 目錄下,把 modules 配置成:

modules:['./src/components','node_modules']

後,你能夠簡單經過 import 'button' 導入。

descriptionFiles

resolve.descriptionFiles 配置描述第三方模塊的文件名稱,也就是 package.json 文件。默認以下:

descriptionFiles: ['package.json']

enforceExtension

resolve.enforceExtension 若是配置爲 true 全部導入語句都必需要帶文件後綴, 例如開啓前 import './foo' 能正常工做,開啓後就必須寫成 import './foo.js'

enforceModuleExtension

enforceModuleExtensionenforceExtension 做用相似,但 enforceModuleExtension 只對 node_modules 下的模塊生效。

enforceModuleExtension 一般搭配 enforceExtension 使用,在 enforceExtension:true 時,由於安裝的第三方模塊中大多數導入語句沒帶文件後綴, 因此這時經過配置 enforceModuleExtension:false 來兼容第三方模塊。

Plugins

Plugin 用於擴展 Webpack 功能,各類各樣的 Plugin 幾乎讓 Webpack 能夠作任何構建相關的事情。

配置 Plugin

Plugin 的配置很簡單,plugins 配置項接受一個數組,數組裏每一項都是一個要使用的 Plugin 的實例,Plugin 須要的參數經過構造函數傳入。

const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');

module.exports = {
  plugins: [
    // 全部頁面都會用到的公共代碼提取到 common 代碼塊中
    new CommonsChunkPlugin({
      name: 'common',
      chunks: ['a', 'b']
    }),
  ]
};

使用 Plugin 的難點在於掌握 Plugin 自己提供的配置項,而不是如何在 Webpack 中接入 Plugin。

DevServer

要配置 DevServer ,除了在配置文件裏經過 devServer 傳入參數外,還能夠經過命令行參數傳入。 注意只有在經過 DevServer 去啓動 Webpack 時配置文件裏 devServer 纔會生效,由於這些參數所對應的功能都是 DevServer 提供的,Webpack 自己並不認識 devServer 配置項。

hot

devServer.hot 配置是否啓用模塊熱替換功能。

DevServer 默認的行爲是在發現源代碼被更新後會經過自動刷新整個頁面來作到實時預覽,開啓模塊熱替換功能後將在不刷新整個頁面的狀況下經過用新模塊替換老模塊來作到實時預覽。

inline

DevServer 的實時預覽功能依賴一個注入到頁面裏的代理客戶端去接受來自 DevServer 的命令和負責刷新網頁的工做。

devServer.inline 用於配置是否自動注入這個代理客戶端到將運行在頁面裏的 Chunk 裏去,默認是會自動注入。 DevServer 會根據你是否開啓 inline 來調整它的自動刷新策略:

  • 若是開啓 inline,DevServer 會在構建完變化後的代碼時經過代理客戶端控制網頁刷新。
  • 若是關閉 inline,DevServer 將沒法直接控制要開發的網頁。這時它會經過 iframe 的方式去運行要開發的網頁,當構建完變化後的代碼時經過刷新 iframe 來實現實時預覽。

若是你想使用 DevServer 去自動刷新網頁實現實時預覽,最方便的方法是直接開啓 inline

historyApiFallback

devServer.historyApiFallback 用於方便的開發使用了 HTML5 History API 的單頁應用。

這類單頁應用要求服務器在針對任何命中的路由時都返回一個對應的 HTML 文件,例如在訪問 http://localhost/userhttp://localhost/home 時都返回 index.html 文件, 瀏覽器端的 JavaScript 代碼會從 URL 裏解析出當前頁面的狀態,顯示出對應的界面。

配置 historyApiFallback 最簡單的作法是:

historyApiFallback: true

這會致使任何請求都會返回 index.html 文件,這隻能用於只有一個 HTML 文件的應用。

若是你的應用由多個單頁應用組成,這就須要 DevServer 根據不一樣的請求來返回不一樣的 HTML 文件,配置以下:

historyApiFallback: {
  // 使用正則匹配命中路由
  rewrites: [
    // /user 開頭的都返回 user.html
    { from: /^\/user/, to: '/user.html' },
    { from: /^\/game/, to: '/game.html' },
    // 其它的都返回 index.html
    { from: /./, to: '/index.html' },
  ]
}

contentBase

devServer.contentBase 配置 DevServer HTTP 服務器的文件根目錄。 默認狀況下爲當前執行目錄,一般是項目根目錄,全部通常狀況下你沒必要設置它,除非你有額外的文件須要被 DevServer 服務。 例如你想把項目根目錄下的 public 目錄設置成 DevServer 服務器的文件根目錄,你能夠這樣配置:

devServer:{
  contentBase: path.join(__dirname, 'public')
}

這裏須要指出可能會讓你疑惑的地方,DevServer 服務器經過 HTTP 服務暴露出的文件分爲兩類:

  • 暴露本地文件。
  • 暴露 Webpack 構建出的結果,因爲構建出的結果交給了 DevServer,因此你在使用了 DevServer 時在本地找不到構建出的文件。

contentBase 只能用來配置暴露本地文件的規則,你能夠經過 contentBase:false 來關閉暴露本地文件。

headers

devServer.headers 配置項能夠在 HTTP 響應中注入一些 HTTP 響應頭,使用以下:

devServer:{
  headers: {
    'X-foo':'bar'
  }
}

host

devServer.host 配置項用於配置 DevServer 服務監聽的地址。

例如你想要局域網中的其它設備訪問你本地的服務,能夠在啓動 DevServer 時帶上 --host 0.0.0.0host 的默認值是 127.0.0.1 即只有本地能夠訪問 DevServer 的 HTTP 服務。

port

devServer.port 配置項用於配置 DevServer 服務監聽的端口,默認使用 8080 端口。 若是 8080 端口已經被其它程序佔有就使用 8081,若是 8081 仍是被佔用就使用 8082,以此類推。

allowedHosts

devServer.allowedHosts 配置一個白名單列表,只有 HTTP 請求的 HOST 在列表裏才正常返回,使用以下:

allowedHosts: [
  // 匹配單個域名
  'host.com',
  'sub.host.com',
  // host2.com 和全部的子域名 *.host2.com 都將匹配
  '.host2.com'
]

disableHostCheck

devServer.disableHostCheck 配置項用於配置是否關閉用於 DNS 重綁定的 HTTP 請求的 HOST 檢查。

DevServer 默認只接受來自本地的請求,關閉後能夠接受來自任何 HOST 的請求。 它一般用於搭配 --host 0.0.0.0 使用,由於你想要其它設備訪問你本地的服務,但訪問時是直接經過 IP 地址訪問而不是 HOST 訪問,因此須要關閉 HOST 檢查。

https

DevServer 默認使用 HTTP 協議服務,它也能經過 HTTPS 協議服務。 有些狀況下你必須使用 HTTPS,例如 HTTP2 和 Service Worker 就必須運行在 HTTPS 之上。 要切換成 HTTPS 服務,最簡單的方式是:

devServer:{
  https: true
}

DevServer 會自動的爲你生成一份 HTTPS 證書。

若是你想用本身的證書能夠這樣配置:

devServer:{
  https: {
    key: fs.readFileSync('path/to/server.key'),
    cert: fs.readFileSync('path/to/server.crt'),
    ca: fs.readFileSync('path/to/ca.pem')
  }
}

clientLogLevel

devServer.clientLogLevel 配置在客戶端的日誌等級,這會影響到你在瀏覽器開發者工具控制檯裏看到的日誌內容。

clientLogLevel枚舉類型,可取以下之一的值 none | error | warning | info。 默認爲 info 級別,即輸出全部類型的日誌,設置成 none 能夠不輸出任何日誌。

compress

devServer.compress 配置是否啓用 gzip 壓縮。boolean 爲類型,默認爲 false

open

devServer.open 用於在 DevServer 啓動且第一次構建完時自動用你係統上默認的瀏覽器去打開要開發的網頁。 同時還提供 devServer.openPage 配置項用於打開指定 URL 的網頁。

其它配置項

Target

target 配置項可讓 Webpack 構建出針對不一樣運行環境的代碼。 target 能夠是如下之一:

target值 描述
web 針對瀏覽器 (默認),全部代碼都集中在一個文件裏
node 針對 Node.js,使用 require 語句加載 Chunk 代碼
async-node 針對 Node.js,異步加載 Chunk 代碼
webworker 針對 WebWorker
electron-main 針對 Electron 主線程
electron-renderer 針對 Electron 渲染線程

例如當你設置 target:'node' 時,源代碼中導入 Node.js 原生模塊的語句 require('fs') 將會被保留,fs 模塊的內容不會打包進 Chunk 裏。

Devtool

devtool 配置 Webpack 如何生成 Source Map,默認值是 false 即不生成 Source Map,想爲構建出的代碼生成 Source Map 以方便調試,能夠這樣配置:

module.export = {
  devtool: 'source-map'
}

Watch 和 WatchOptions

前面介紹過 Webpack 的監聽模式,它支持監聽文件更新,在文件發生變化時從新編譯。在使用 Webpack 時監聽模式默認是關閉的,想打開須要以下配置:

module.export = {
  watch: true
}

在使用 DevServer 時,監聽模式默認是開啓的。

除此以外,Webpack 還提供了 watchOptions 配置項去更靈活的控制監聽模式,使用以下:

module.export = {
  // 只有在開啓監聽模式時,watchOptions 纔有意義
  // 默認爲 false,也就是不開啓
  watch: true,
  // 監聽模式運行時的參數
  // 在開啓監聽模式時,纔有意義
  watchOptions: {
    // 不監聽的文件或文件夾,支持正則匹配
    // 默認爲空
    ignored: /node_modules/,
    // 監聽到變化發生後會等300ms再去執行動做,防止文件更新太快致使從新編譯頻率過高
    // 默認爲 300ms  
    aggregateTimeout: 300,
    // 判斷文件是否發生變化是經過不停的去詢問系統指定文件有沒有變化實現的
    // 默認每1000豪秒去問1次
    poll: 1000
  }
}

Externals

Externals 用來告訴 Webpack 要構建的代碼中使用了哪些不用被打包的模塊,也就是說這些模版是外部環境提供的,Webpack 在打包時能夠忽略它們。

有些 JavaScript 運行環境可能內置了一些全局變量或者模塊,例如在你的 HTML HEAD 標籤裏經過如下代碼:

<script src="path/to/jquery.js"></script>

引入 jQuery 後,全局變量 jQuery 就會被注入到網頁的 JavaScript 運行環境裏。

若是想在使用模塊化的源代碼裏導入和使用 jQuery,可能須要這樣:

import $ from 'jquery';
$('.my-element');

構建後你會發現輸出的 Chunk 裏包含的 jQuery 庫的內容,這致使 jQuery 庫出現了2次,浪費加載流量,最好是 Chunk 裏不會包含 jQuery 庫的內容。

Externals 配置項就是爲了解決這個問題。

經過 externals 能夠告訴 Webpack JavaScript 運行環境已經內置了那些全局變量,針對這些全局變量不用打包進代碼中而是直接使用全局變量。 要解決以上問題,能夠這樣配置 externals

module.export = {
  externals: {
    // 把導入語句裏的 jquery 替換成運行環境裏的全局變量 jQuery
    jquery: 'jQuery'
  }
}

ResolveLoader

ResolveLoader 用來告訴 Webpack 如何去尋找 Loader,由於在使用 Loader 時是經過其包名稱去引用的, Webpack 須要根據配置的 Loader 包名去找到 Loader 的實際代碼,以調用 Loader 去處理源文件。

ResolveLoader 的默認配置以下:

module.exports = {
  resolveLoader:{
    // 去哪一個目錄下尋找 Loader
    modules: ['node_modules'],
    // 入口文件的後綴
    extensions: ['.js', '.json'],
    // 指明入口文件位置的字段
    mainFields: ['loader', 'main']
  }
}

該配置項經常使用於加載本地的 Loader。

總體配置結構

以前的章節分別講述了每一個配置項的具體含義,但沒有描述它們所處的位置和數據結構,下面經過一份代碼來描述清楚:

const path = require('path');

module.exports = {
    // entry 表示 入口,Webpack 執行構建的第一步將從 Entry 開始,可抽象成輸入。
    // 類型能夠是 string | object | array
    entry: './app/entry', // 只有1個入口,入口只有1個文件
    entry: ['./app/entry1', './app/entry2'], // 只有1個入口,入口有2個文件
    entry: { // 有2個入口
        a: './app/entry-a',
        b: ['./app/entry-b1', './app/entry-b2']
    },

    // 如何輸出結果:在 Webpack 通過一系列處理後,如何輸出最終想要的代碼。
    output: {
        // 輸出文件存放的目錄,必須是 string 類型的絕對路徑。
        path: path.resolve(__dirname, 'dist'),

        // 輸出文件的名稱
        filename: 'bundle.js', // 完整的名稱
        filename: '[name].js', // 當配置了多個 entry 時,經過名稱模版爲不一樣的 entry 生成不一樣的文件名稱
        filename: '[chunkhash].js', // 根據文件內容 hash 值生成文件名稱,用於瀏覽器長時間緩存文件

        // 發佈到線上的全部資源的 URL 前綴,string 類型
        publicPath: '/assets/', // 放到指定目錄下
        publicPath: '', // 放到根目錄下
        publicPath: 'https://cdn.example.com/', // 放到 CDN 上去

        // 導出庫的名稱,string 類型
        // 不填它時,默認輸出格式是匿名的當即執行函數
        library: 'MyLibrary',

        // 導出庫的類型,枚舉類型,默認是 var
        // 能夠是 umd | umd2 | commonjs2 | commonjs | amd | this | var | assign | window | global | jsonp ,
        libraryTarget: 'umd',

        // 是否包含有用的文件路徑信息到生成的代碼裏去,boolean 類型
        pathinfo: true,

        // 附加 Chunk 的文件名稱
        chunkFilename: '[id].js',
        chunkFilename: '[chunkhash].js',

        // JSONP 異步加載資源時的回調函數名稱,須要和服務端搭配使用
        jsonpFunction: 'myWebpackJsonp',

        // 生成的 Source Map 文件名稱
        sourceMapFilename: '[file].map',

        // 瀏覽器開發者工具裏顯示的源碼模塊名稱
        devtoolModuleFilenameTemplate: 'webpack:///[resource-path]',

        // 異步加載跨域的資源時使用的方式
        crossOriginLoading: 'use-credentials',
        crossOriginLoading: 'anonymous',
        crossOriginLoading: false,
    },

    // 配置模塊相關
    module: {
        rules: [ // 配置 Loader
            {
                test: /\.jsx?$/, // 正則匹配命中要使用 Loader 的文件
                include: [ // 只會命中這裏面的文件
                    path.resolve(__dirname, 'app')
                ],
                exclude: [ // 忽略這裏面的文件
                    path.resolve(__dirname, 'app/demo-files')
                ],
                use: [ // 使用那些 Loader,有前後次序,從後往前執行
                    'style-loader', // 直接使用 Loader 的名稱
                    {
                        loader: 'css-loader',
                        options: { // 給 html-loader 傳一些參數
                        }
                    }
                ]
            },
        ],
        noParse: [ // 不用解析和處理的模塊
            /special-library\.js$/  // 用正則匹配
        ],
    },

    // 配置插件
    plugins: [],

    // 配置尋找模塊的規則
    resolve: {
        modules: [ // 尋找模塊的根目錄,array 類型,默認以 node_modules 爲根目錄
            'node_modules',
            path.resolve(__dirname, 'app')
        ],
        extensions: ['.js', '.json', '.jsx', '.css'], // 模塊的後綴名
        alias: { // 模塊別名配置,用於映射模塊
            // 把 'module' 映射 'new-module',一樣的 'module/path/file' 也會被映射成 'new-module/path/file'
            'module': 'new-module',
            // 使用結尾符號 $ 後,把 'only-module' 映射成 'new-module',
            // 可是不像上面的,'module/path/file' 不會被映射成 'new-module/path/file'
            'only-module$': 'new-module',
        },
        alias: [ // alias 還支持使用數組來更詳細的配置
            {
                name: 'module', // 老的模塊
                alias: 'new-module', // 新的模塊
                // 是不是隻映射模塊,若是是 true 只有 'module' 會被映射,若是是 false 'module/inner/path' 也會被映射
                onlyModule: true,
            }
        ],
        symlinks: true, // 是否跟隨文件軟連接去搜尋模塊的路徑
        descriptionFiles: ['package.json'], // 模塊的描述文件
        mainFields: ['main'], // 模塊的描述文件裏的描述入口的文件的字段名稱
        enforceExtension: false, // 是否強制導入語句必需要寫明文件後綴
    },

    // 輸出文件性能檢查配置
    performance: {
        hints: 'warning', // 有性能問題時輸出警告
        hints: 'error', // 有性能問題時輸出錯誤
        hints: false, // 關閉性能檢查
        maxAssetSize: 200000, // 最大文件大小 (單位 bytes)
        maxEntrypointSize: 400000, // 最大入口文件大小 (單位 bytes)
        assetFilter: function (assetFilename) { // 過濾要檢查的文件
            return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
        }
    },

    devtool: 'source-map', // 配置 source-map 類型

    context: __dirname, // Webpack 使用的根目錄,string 類型必須是絕對路徑

    // 配置輸出代碼的運行環境
    target: 'web', // 瀏覽器,默認
    target: 'webworker', // WebWorker
    target: 'node', // Node.js,使用 `require` 語句加載 Chunk 代碼
    target: 'async-node', // Node.js,異步加載 Chunk 代碼
    target: 'node-webkit', // nw.js
    target: 'electron-main', // electron, 主線程
    target: 'electron-renderer', // electron, 渲染線程

    externals: { // 使用來自 JavaScript 運行環境提供的全局變量
        jquery: 'jQuery'
    },

    stats: { // 控制檯輸出日誌控制
        assets: true,
        colors: true,
        errors: true,
        errorDetails: true,
        hash: true,
    },

    devServer: { // DevServer 相關的配置
        proxy: { // 代理到後端服務接口
            '/api': 'http://localhost:3000'
        },
        contentBase: path.join(__dirname, 'public'), // 配置 DevServer HTTP 服務器的文件根目錄
        compress: true, // 是否開啓 gzip 壓縮
        historyApiFallback: true, // 是否開發 HTML5 History API 網頁
        hot: true, // 是否開啓模塊熱替換功能
        https: false, // 是否開啓 HTTPS 模式
    },

    profile: true, // 是否捕捉 Webpack 構建的性能信息,用於分析什麼緣由致使構建性能不佳

    cache: false, // 是否啓用緩存提高構建速度

    watch: true, // 是否開始
    watchOptions: { // 監聽模式選項
        // 不監聽的文件或文件夾,支持正則匹配。默認爲空
        ignored: /node_modules/,
        // 監聽到變化發生後會等300ms再去執行動做,防止文件更新太快致使從新編譯頻率過高
        // 默認爲300ms
        aggregateTimeout: 300,
        // 判斷文件是否發生變化是不停的去詢問系統指定文件有沒有變化,默認每秒問 1000 次
        poll: 1000
    },
};

多種配置類型

除了經過導出一個 Object 來描述 Webpack 所需的配置外,還有其它更靈活的方式,以簡化不一樣場景的配置。

導出一個 Function

在大多數時候你須要從同一份源代碼中構建出多份代碼,例如一份用於開發時,一份用於發佈到線上。

若是採用導出一個 Object 來描述 Webpack 所需的配置的方法,須要寫兩個文件。 一個用於開發環境,一個用於線上環境。再在啓動時經過 webpack --config webpack.config.js 指定使用哪一個配置文件。

採用導出一個 Function 的方式,能經過 JavaScript 靈活的控制配置,作到只寫一個配置文件就能完成以上要求。

導出一個 Function 的使用方式以下:

const path = require('path');
const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');

module.exports = function (env = {}, argv) {
    const plugins = [];

    const isProduction = env['production'];

    // 在生成環境才壓縮
    if (isProduction) {
        plugins.push(
            // 壓縮輸出的 JS 代碼
            new UglifyJsPlugin()
        )
    }

    return {
        plugins: plugins,
        // 在生成環境不輸出 Source Map
        devtool: isProduction ? undefined : 'source-map',
    };
};

在運行 Webpack 時,會給這個函數傳入2個參數,分別是:

  1. env:當前運行時的 Webpack 專屬環境變量,env 是一個 Object。讀取時直接訪問 Object 的屬性,設置它須要在啓動 Webpack 時帶上參數。例如啓動命令是 webpack --env.production --env.bao=foo 時,則 env 的值是 {"production":"true","bao":"foo"}
  2. argv:表明在啓動 Webpack 時全部經過命令行傳入的參數,例如 --config、--env、--devtool,能夠經過 webpack -h 列出全部 Webpack 支持的命令行參數。

就以上配置文件而言,在開發時執行命令 webpack 構建出方便調試的代碼,在須要構建出發佈到線上的代碼時執行 webpack --env.production 構建出壓縮的代碼。

導出一個返回 Promise 的函數

在有些狀況下你不能以同步的方式返回一個描述配置的 Object,Webpack 還支持導出一個返回 Promise 的函數,使用以下:

module.exports = function(env = {}, argv) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        // ...
      })
    }, 5000)
  })
}

導出多份配置

除了只導出一份配置外,Webpack 還支持導出一個數組,數組中能夠包含每份配置,而且每份配置都會執行一遍構建。

使用以下:

module.exports = [
  // 採用 Object 描述的一份配置
  {
    // ...
  },
  // 採用函數描述的一份配置
  function() {
    return {
      // ...
    }
  },
  // 採用異步函數描述的一份配置
  function() {
    return Promise();
  }
]

以上配置會致使 Webpack 針對這三份配置執行三次不一樣的構建。

這特別適合於用 Webpack 構建一個要上傳到 Npm 倉庫的庫,由於庫中可能須要包含多種模塊化格式的代碼,例如 CommonJS、UMD。

配置總結

從前面的配置看來選項不少,Webpack 內置了不少功能。

你沒必要都記住它們,只須要大概明白 Webpack 原理和核心概念去判斷選項大體屬於哪一個大模塊下,再去查詳細的使用文檔。

一般你可用以下經驗去判斷如何配置 Webpack:

  • 想讓源文件加入到構建流程中去被 Webpack 控制,配置 entry
  • 想自定義輸出文件的位置和名稱,配置 output
  • 想自定義尋找依賴模塊時的策略,配置 resolve
  • 想自定義解析和轉換文件的策略,配置 module,一般是配置 module.rules 裏的 Loader。
  • 其它的大部分需求可能要經過 Plugin 去實現,配置 plugin
相關文章
相關標籤/搜索