看啥雙拱門,來學 webpack 3啊


webpack 是前端開發者一個跨不過去的編譯工具。不過由於他的快速迭代,讓很多同學在學一個版本的時候,下一個新版本就發布了,讓人感覺非常蛋疼和無奈:

「我是誰,我在幹嘛,我要做什麼?」

不過,如果你已經掌握 webpack 比較老的版本,對於新版本的學習而言,應該只需要 0.5 天的工作量就可以完成。因為,其基本理念都是以 JS 為中心,增加的只是其輔助特性。

webpack 到目前為止已經走過了三個大版本,每個版本之間都,增加了非常多可用的特性,但是每個版本的具體配置項都有些差異,很容易讓老版本用戶形成依賴。現在 webpack 3 已經走到了 3.8 版本。現版本對於 webpack 2.x 的配置項來說,最突出的就是 loader 的寫法的改變和內置了 webpack-dev-server 的特性。

所以,本文將帶你全面了解一下 webpack 3.x 版本的新特性和一些常用的 webpack 3.x 的配置。

entry-入口 JS 配置

entry 選項主要用來定製 webpack 引用 JS 文件的主入口,它可以配置多個值來定義多個 JS 主文件入口。具體配置項為:

entry可以是三種值[string | object | array]

  • 字元串:如 entry:./src/index.js,字元串也可以是函數的返回值,如entry: () => ./demo。
  • 數組形式,如[react,react-dom],可以把數組中的多個文件打包轉換為一個chunk;
  1. entry: ["./app/entry1", "./app/entry2"],
  • 對象形式,如果我們需要配置的是多頁應用,或者我們要抽離出指定的模塊做為公共代碼,就需要採用這種形式了,屬性名是佔位符[name]的值,屬性值可以是上面的字元串和數組,如下:
  1. // 值得注意的是入口文件有幾個就會生成幾個獨立的依賴圖譜。
  2. entry:{
  3. main:./src/index.js,
  4. second:./src/index2.js,
  5. vendor: [react,react-dom] // 將 react,react-dom 一起打包
  6. }

有時候,開發者還會利用 require.resolve(route) 來在 entry 中引入具體路徑的文件。該方法只是用來作為路徑模塊的搜索,其返回的是指定文件的絕對路徑名。解析機制有點類似,require(route)

  1. entry: [
  2. require.resolve(react-dev-utils/webpackHotDevClient),
  3. require.resolve(./polyfills), //返回 /Users/villainHR/Desktop/file/polyfills/index.js
  4. require.resolve(react-error-overlay),
  5. src/index.js
  6. ],

entry 還可以接受 function 返回的 string,array,object 值:

  1. entry: () => ./demo

context 上下文基礎目錄

context 用來設置 entry 和 loaders 的上下文索引依賴。默認情況下,entry 等參考的是當前目錄(CWD)

  1. // 手動設置
  2. context: path.resolve(__dirname, "app")

通過,entry 設置的相對路徑,會優先尋找 ./app 路徑下的文件。

output-輸出 JS 配置

基礎配置項

output 裡面又很多基本的配置項,基礎配置項有:

  1. output:{
  2. path: path.join(__dirname,./dist),
  3. filename:js/bundle-[name]-[hash].js,
  4. chunkFilename:js/[name].chunk.js,
  5. chunkLoadTimeout: 12000,
  6. publicPath:/dist/
  7. }
  • path: 用來決定輸出目錄。通常結合 path.join/resolve() 設置絕對路徑。
  • filename[alias: name]: 決定具體輸出的文件名,上面 output 選項中,可以用到很多全局的佔位符,比如:[name]、[hash] 等。
  • chunkFilename: 主要是用來對沒在 entry 中定義的文件來設定具體的輸出目錄和文字的。常常用來按需的非同步載入當中,例如:
  1. require.ensure(["modules/tips.jsx"], function(require) {
  2. var a = require("modules/tips.jsx");
  3. // ...
  4. }, tips);

這裡,chunkFilename 就可以將 tips.jsx 具體輸出為 js/tips.chunk.js。

  • publicPath:並不是用來輸出打包路徑,而是在 runtime 的時候,手動修改 src 引用路徑的。常常配合 css/style/html-loader 來修改。例如,會將: src="picture.jpg"Re-writes?src="/assets/picture.jpg"。相當於,手動修改請求 url 的地址。
  • chunkLoadTimeout[Number]: 設置 webpack chunk 的請求超時時間。// TODO

上面也提到了,在 output 中會存在一起全局佔位符,比如::[name]、[hash] 等。代表的含義為:

  • [name]:代表打包後文件的名稱,在entry或代碼中(之後會看到)確定;
  • [id]:webpack給塊分配的內部chunk id,如果你沒有隱藏,你能在打包後的命令行中看到;
  • [hash]:每次構建過程中,生成的唯一 hash 值;(每次都變)
  • [chunkhash]: 依據於打包生成文件內容的 hash 值,內容不變,值不變;
  • [ext]: 資源擴展名,如js,jsx,png等等;

hash 控制

在全局佔位符中,有一個特殊的值 [hash]。我們可以在 webpack 中,針對 hash 輸出做相關的控制。(這裡會一併影響到 [chunkhash])

  • output.hashDigest: 具體輸出 hash 的 encoding,有 hex, latin1 or base64。默認為 hex
  • output.hashDigestLength: 輸出 hash 具體長度。默認為 20。
  • output.hashFunction: 引入文件的具體演算法,默認為 md5。
  • output.hashSalt: 給 hash 加鹽,很少用。

如果,有使用 ExtractTextWebpackPlugin 的話,它會通過 contenthash 選項,直接覆蓋上面 4 個的 hash 選項。

自定義庫的打包

在 webpack 中,如果想要自己寫一個包並發布的話,就需要獨立打包成為一個 library。這裡,在 webpack 中有指定的配置項,和我們平常寫的 webpack 打包項目不同。

獨立打包一個庫,常常會遇到應用外部庫的事情,你可以選擇大伯啊,也可以選擇不打包。這裡,除了 output 選項之外,還需要 externals 選項。下面內容主要對 output 裡面內容做詳解解釋,而 externals 會放到後面進行講解。

externals 主要是用來解決外部庫打包問題,基本設置為:

  1. externals: {
  2. key: "value"
  3. },
  • value: 用來指明 外部庫 通過全局(window,global)的方式暴露的模塊名。例如:
  1. window.jQuery = {
  2. //xxx
  3. }

webapck 會直接處理 window 或者 global 對象進行模塊化包裹。否則,則不會對指定模塊進行 externals 處理。這點非常關鍵。

  • key: 具體指代,在經過 value 處理過後的外部庫,能夠在 webpack 通過 require 或者 import 的包名,例如:
  1. externals: {
  2. $: "jQuery"
  3. }
  4. // index.js
  5. var $ = require("$");

在 output 中主要有 4 個關鍵選項設置值:

  • output.library[String]: 用來設置通過,CDN 引入時的全局 Name。對於,require/import 引入的沒影響(模塊引入具體是根據具體代碼中的 exports 來決定)
  • output.libraryExport: 限定通過模塊具體暴露的介面。例如:
  1. libraryExport: ["MyModule", "MySubModule"]; // 只能到處前面兩個模塊

最重要的模塊輸出選項應該是 libraryTarget,它是用來決定模塊以何種規範輸出,在全局變數的 Name 是啥。其基本格式為:

  1. output.libraryTarget[String]: var | assign | this | window | global | commonjs | commonjs2 | amd | umd

上面那些全部是都是可選項值。這裡先告訴大家,在現代 JS 的寫法中,最後一個 umd 的選項是最常用的。如果你還是想通過 script 標籤引入的話,則前面 5 個比較適合給你靈活自定義。當然, umd 選項也是適合的,不過靈活性差了點。

上面選項可以分為三種類型:

  • 全局變數型[var,assign]:用來表示模塊導出,是直接在全局中定義變數。比如,jQuery 的 $,不需要額外引入處理。
  • 對象綁定型[this,window,global,commonjs]:用來將導出的模塊,綁定到具體的對象中。比如,jQuery 的 window[$],this[$] 這樣的引入綁定。
  • 模塊綁定型[commonjs2,amd,umd]: 需要通過,全局 require() 方法引入的模塊。

libraryTarget 常常和 library 選項一起結合使用。下面內容,統一 library 設置為 Villainhr

  1. output.library: Villainhr

接下來,我們具體一個一個來細分講解一下:

全局變數型

var

  1. output.libraryTarget: var // 默認值

在 js 腳本實際編譯結果為:

  1. var Villainhr = _entry_return_;
  2. // In a separate script...
  3. Villainhr.doSomething();

通過 var 變數定義關鍵字來申明一個變數,將導出的模塊直接複製過去。

assign

  1. output.libraryTarget: assign

編譯結果為:

  1. Villainhr = _entry_return_;
  2. // In a separate script...
  3. Villainhr.doSomething();

這裡,就直接全局綁定,如果前面已經定義過 Villainhr 則會默認覆蓋。其實和 var 定義沒啥區別。

對象綁定型

對象綁定則是直接通過 Object 的方法,將導出的模塊直接綁定到具體對象:

this

  1. output.libraryTarget: "this"

編譯結果為:

  1. this["Villainhr"] = _entry_return_;
  2. // In a separate script...
  3. this.Villainhr.doSomething();
  4. Villainhr.doSomething(); // if this is window

導出的模塊,直接綁定在 this 上,在全局中直接使用 this.xxx 來進行模塊調用。

window

  1. output.libraryTarget: "window"

編譯結果為:

  1. window["Villainhr"] = _entry_return_;
  2. window.Villainhr.doSomething();
  3. // or
  4. Villainhr.doSomething();

這裡就是通常的導出方案,直接將輸出模塊綁定到 window 對象上。其實也相當於 var 綁定,直接通過模塊名使用。

global

  1. output.libraryTarget: "global"

編譯結果為:

  1. global["Villainhr"] = _entry_return_;

直接通過 global 對象調用,這個通常用在 NodeJS 的環境。

commonjs

  1. output.libraryTarget: "commonjs"

編譯結果為:

  1. exports["Villainhr"] = _entry_return_;
  2. require("Villainhr").doSomething();

在引用調用時,直接通過 require() 方法引入。

模塊綁定型

模塊綁定型有 commonjs2,amd,umd 這三個選項。他們具體的規範就是根據選項值對應的規範來的—— CommonJSAMD

commonjs2

編譯結果為:

  1. module.exports = _entry_return_;

這和 commonjs 類似,不過遵循的是 commonjs2 的規範,是直接將其賦值到 module.exports 上的。具體使用還是需要 require() 引用:

  1. require("Villainhr").doSomething();

amd

編譯結果為:

  1. define("Villainhr", [], function() {
  2. // main body
  3. });

這裡通過 amd 非同步載入的規範,來導出具體的文件模塊。具體使用是通過 require 方法直接引入使用:

  1. require([Villainhr], function(Villainhr) {
  2. // xxx
  3. });

umd

這個模塊標準應該屬於最方便的一種,它實際上集合了 commonjs1/2,amd,this 的綁定方式。具體編譯結果為:

  1. (function webpackUniversalModuleDefinition(root, factory) {
  2. if(typeof exports === object && typeof module === object)
  3. module.exports = factory();
  4. else if(typeof define === function && define.amd)
  5. define([], factory);
  6. else if(typeof exports === object)
  7. exports["Villainhr"] = factory();
  8. else
  9. root["Villainhr"] = factory();
  10. })(this, function() {
  11. //what this module returns is what your entry chunk returns
  12. });

這裡其實一共有 4 個綁定,不過它會優先進行綁定選擇,具體順序是:

commonjs2 > AMD > commonjs > this

也就是 4 選一,如果已經通過 commonjs2 引入的話,則不能在通過其它方式使用。比如,this.Villainhr 這樣是訪問不到的。

module 編譯設置

在 module 選項主要就是用設置 webpack 中常用的 loaders。通過 rules 規則來匹配具體應用的文件和 loaders。

noParse 防止預編譯

noParse 主要用來設置相關的匹配規則,來防止 webpack loaders 對某些文件進行預編譯。

基本設置為:

  1. noParse: RegExp | [RegExp] | function

通常,設置的值可以直接為:

  1. noParse: /jquery|lodash/
  2. // or
  3. noParse: function(content) {
  4. return /jquery|lodash/.test(content);
  5. }

這樣,jquery 和 loadsh 就不會被 webpack 中的 loaders 捕獲並編譯了。

rules 設置匹配規則

module.rules 的選項具體是用來設置 loaders 匹配文件的規則。其基本接受類型為 [Array]。

  1. module: {
  2. rules: [{
  3. test: /.js$/,
  4. exclude: /node_modules/dist/,
  5. loader: babel-loader
  6. }]
  7. },

rules 裡面的每個對象都決定了 loaders 的具體類型以及 loaders 作用的具體文件。在 webpack3 中,繼承了 webpack2 的基本限定功能,比如 excludeinclude等,還額外加入了通過 query 匹配,以及多個 query 的 loader 匹配規則。

文件匹配

在每一條具體的 rule 裡面,用來進行匹配文件的選項有:Rule.test, Rule.exclude, Rule.include, Rule.and, Rule.or, Rule.not。

上面三個對象其實掛載到的是 Rule.resource 對象上的,你可以直接寫到 Rule 上,也可以寫到 Rule.resource 上。這裡簡單起見,以 rule 為基準。他們的值統一都為 condition。這個概念,是 webpack 匹配文件提出的。

condition

主要用來設置匹配文件的條件規則。可接受的值有:

  1. condition: [string | RegExp | function | array | object]
  • string: 匹配文件的絕對路徑
  • RegExp: 匹配文件的正則
  • function: 參數為 input 的路徑,根據返回的 boolean 來決定是否匹配。
  • array: 裡面可以傳入多個 condition 匹配規則。
  • object: 不常用,用來匹配 key。

接下來,我們來看一下 condition 在各個選項值中的具體實踐。

test

test 用來匹配符合條件的 input。設置的值為:

  1. test: [condition]

最常用的還是直接寫入正則:

  1. test: /.jsx$/

include

和 test 一樣,也是用來匹配符合條件的 input,主要用途是用來設置 依賴文件索引目錄。在 include 中,通常設置 string 或者 array of strings。

  1. // 在 entry css 文件中,只能在 app/styles 以及 vendor/styles 中索引依賴文件
  2. {
  3. test: /.css$/,
  4. include: [
  5. path.resolve(__dirname, "app/styles"),
  6. path.resolve(__dirname, "vendor/styles")
  7. ]
  8. }

exclude

設置依賴文件不會存在的目錄。

實際上,上面三個命令是一起配合來提高編譯速度的。因為默認情況下,webpack loaders 會對所有引入的文件進行 loader 編譯,當然,對於 node_modules 裡面成熟的庫,我們沒比較在進行額外的 loader 編譯,直接將其 bundle 即可。

常用設置可以為:

  1. test: /.js$/,
  2. loader: babel-loader,
  3. include: [
  4. path.resolve(__dirname, "app/src"),
  5. path.resolve(__dirname, "app/test")
  6. ],
  7. exclude: /node_modules/ // 排除編譯 node_modules 中的文件

剩下的 ornot 也是用來設置規則,根據名字大家也可以猜出這兩者的含義:

  • or[condition]:滿足其中一種條件即可。例如: or:[/.*src/index.*/,/.*abc.*/]
  • not[condition]:都不滿足所有的條件。設置方法同上

query 匹配

query 匹配具體指的是匹配 url 中的 ? 後的字元串。當然,我們也可以在 test 中通過正則來寫,不過,webpack3 既然已經提供了 query 的選項,我們也可以直接使用它的配置-- resourceQuery

resourceQuery 用來設置匹配 query 的規則,接受的內容是 condition。不過,一般直接設置 正則 或者 string 就行:

  1. // 匹配 query 含有 inline 的路徑,例如:./villainhr.css?inline
  2. {
  3. test: /.css$/,
  4. resourceQuery: /inline/,
  5. use: url-loader
  6. }

另外,如果你想對不同的 query 使用不同的 loader 話,可以直接使用 oneOf 配置。文件資源會默認找到 oneOf 中第一個匹配規則,來調用對應的 loader 處理。

  1. {
  2. test: /.css$/,
  3. oneOf: [
  4. {
  5. resourceQuery: /inline/, // villainHR.css?inline
  6. use: url-loader
  7. },
  8. {
  9. resourceQuery: /external/, // villainHR.css?external
  10. use: file-loader
  11. }
  12. ]
  13. }

loader 編譯設置

在 webpack2 的時候,主要寫法是根據 loadersloader 來進行設定的。不過,在 webpack3 該為根據文件來決定 loader 的載入。這其中,最大的特點就是,將 loaders 替換為了 rules。例如:

  1. module: {
  2. loaders: [
  3. {test: /.css$/, loader: style-loader!css-loader}
  4. ]
  5. }
  6. // 替換為:
  7. module: {
  8. rules: [
  9. {
  10. test: /.css$/, use:[
  11. style-loader,css-loader
  12. ]}
  13. ]
  14. }

按照規範, use 是用來實際引入 loader 的標籤。在 webpack3 時代,還保留了 loader欄位,廢除了 query 欄位,其實可以在 use 中找到替代。

loader

用來定義具體使用的 loader,這裡等同於: use:[loader]。

query

用來限定具體的 loader 使用的配置參數,例如 babel 的配置:

  1. test: /.js$/,
  2. loader: babel-loader,
  3. query: {
  4. presets: [es2015]
  5. }

不過,在 webpack3 中已經廢棄了 query,使用 useoptions 選項:

  1. test: /.js$/,
  2. use:[
  3. {
  4. loader: babel-loader,
  5. options:{
  6. presets: [es2015]
  7. }
  8. }
  9. ]

resolve 模塊解析路徑

resolve 主要是用來解析具體入口文件中,引入依賴文件解析。例如,通過 import/ require 引入的文件:

  1. import foo from demo/test/villainhr.js)
  2. // or
  3. require(demo/test/villainhr.js)

在 webpack 中,提供了 3 種路徑解析方式:

  • 相對路徑:直接根據入口文件的路徑,來對路徑進行解析。相當於 path.resolve() 這個方法。
  1. import "../src/villainhr.js"; // 等同於 path.resolve(__dirname,../src/villainhr.js)
  • 絕對路徑:根據 resolve.modules 設置的參考目錄來進行解析。默認是根據 webpack 所在的目錄。設置了之後,絕對路徑的解析,只會在設置的參考目錄中查找。
  1. modules: [path.resolve(__dirname, "src"), "node_modules"] // 設置絕對路徑搜索目錄
  2. import "/villainhr.js"; // 只會搜索 src/villainhr.js 以及 node_modules/villain.js 。
  • 模塊路徑:直接引入模塊,路徑前面不加任何修飾符號。例如: import"es5";。webpack 的模塊解析規則比上面兩個規則要複雜一點。因為還牽扯到 modulesaliasextensions 等。主要規則為:
    • 搜索 resolve.modules 定義的目錄,如果有 alias 標籤,則還會參考 alias 定義的模塊路徑
    • 檢查引用的路徑是文件
      • 檢查文件是否存在,如果沒有尾綴,則根據 resolve.extensions 定義的尾綴來索引。
    • 檢查引用的路徑是目錄
      • 檢查含有 package.json,根據 resolve.mainFields 定義的欄位來索引文件。
      • 如果不含有 package.json,直接根據 resolve.mainFiles 來索引文件。
      • 具體文件解析,根據 resolve.extensions 來解決。

具體模塊路徑解析,可以參考如下:

  1. # /src/villain.js 存在
  2. import VR from villain;
  3. /** 搜索 /src、node_modules 目錄,
  4. 找到 villain 文件,如果沒有,
  5. 根據 extensions 添加尾綴查詢。
  6. 找到 villain.js
  7. */
  8. # node_modules/es6 模塊存在
  9. import VR from es6;
  10. /** 搜索 /src、node_modules 目錄,
  11. 找到 es6 文件夾
  12. 查看 package.json 文件,
  13. 根據 mainFields 定義的欄位索引 packjson,找到 main 欄位定義的文件。
  14. */
  15. # src/demo/dev/index.js 存在
  16. import VR from ABC/dev;
  17. /** 根據 alias 定義的 ABC 查找,src/demo/ 目錄下的文件
  18. 找到 dev 目錄,裡面沒有 package.json 文件
  19. 根據 mainFiles 定義的文件名和 extensions 定義的尾綴找到 index.js 文件
  20. // webpack.config.js
  21. resolve:{
  22. alias: {
  23. ABC: path.resolve(__dirname, src/demo/)
  24. },
  25. modules: [path.resolve(__dirname, "src"), "node_modules"],
  26. mainFields: ["main"],
  27. mainFiles: ["index"],
  28. extensions: [".js", ".json"]
  29. }

設置模塊索引目錄

在 webpack 中,主要提供 aliasmodules 來進行索引目錄的設置。

  • alias:設置導入路徑的簡便寫法。如果你有大量的文件依賴路徑非常長的話,除了使用 modules 設置參考路徑之外,還可以根據 alias 來設置路徑映射的 key。另外,它還提供了 $ 來進行文件的精確匹配,這個看看文檔就行,感覺不常用。例如:
  1. alias: {
  2. Villainhr: path.resolve(__dirname, src/Villainhr/)
  3. }
  4. # 如果要獲取 src/Villainhr/index.js 的話,可以直接在 import 寫入:
  5. import test from Villainhr/index;
  • modules: 用來設置模塊解析的目錄。這樣,在進行模塊查找的時候,webpack 會優先遍歷你定義的目錄。如果,你還有自定義模塊在 src 目錄下,通過普通模塊解析是找不到的,這時候可以直接在 modules 中添加 src。
  1. modules: [path.resolve(__dirname, "src"), "node_modules"]

package.json 索引解析

和模塊搜索最關鍵的文件是 package.json、在 node 環境下,如果模塊文件中存在 package.json,會根據其 main 欄位定義的文件來進行索引。通過 webpack 設定的模塊索引,可以更靈活的設置查找規則。其中,主要依賴的是 mainFieldsmainFilesextensions 這三個欄位。

  • mainFields[Array]: 接受的是數組,用來設置在 package.json 文件中,用來定義具體查找文件的欄位。例如: mainFields:["browser","module","main"],則會按順序搜索: browsermodulemain 這三個欄位。
  • mainFiles[Array]:如果文件不含有 package.json,則直接根據 mainFiles 定義的文件來索引。
  • extensions[Array]:設置默認文件後綴名。當索引文件時,如果具體路徑沒帶文件名的話,會根據 exntensions定義的內容來獲取。

loader 簡便書寫

modules 指令下,添加具體調用的 loader 一定需要帶上後面的 -loader 尾綴。如果你不想帶,則會默認報錯。不想報錯的話,可以使用 moduleExtensions 指令來說明:

  1. moduleExtensions: [-loader]

這樣,就可以直接在 module 中直接不帶 -loader 引用:

  1. loader: eslint

externals 定義外部依賴

externals 是用來排除某些你並不想打包的庫,並且可以通過 import/ require 在全局環境中調用。這個選項一般是提供給一些開源庫或者組件作者使用的。

相當於,你調用了一個庫,而最終實際打包的文件裡面剔除該庫的存在,只是留下了一個引用介面。

  1. // 剔除 sw-router 庫
  2. externals: {
  3. Router: sw-router
  4. }
  5. // 在代碼中使用
  6. import Router from Router;

它可以接收如下類型:(array object function regex)

  • array:用來過濾打包文件中的子模塊。
  1. // 將 ./math 文件中的 subtract 過濾不打包。
  2. externals: {
  3. subtract: [./math, subtract]
  4. }
  5. // 忽略如下文件打包:
  6. import {subtract} from ./math;
  • object:通過 key 值,來決定在不同模塊規範下,文件暴露的介面名。其中,commonjs 代表 require 模塊規則,amd 則代表 define 模塊規則,root 則是代表該模塊在全局環境,例如 window 訪問的對象名。
  1. externals : {
  2. ex_loadsh : {
  3. commonjs: "lodash", // require(loadsh)
  4. amd: "lodash", // define([loadsh])
  5. root: "_" // window._
  6. }
  7. }

通過 externals 定義之後,我們在不同環境訪問 loadsh 的方式就變為:

  1. // 全局環境
  2. window.ex_loadsh;
  3. // 模塊包中
  4. require(ex_loadsh);
  • function: 前面幾個寫法都是將需要提出的文件寫死,而 function 提供了可以靈活配置的 external list。比如,你想剔除所有 node_modules 包中的庫,而裡面又有幾個包需要引用的話,可以寫為:
  1. externals: [
  2. function(context, request, callback) {
  3. if (/^yourregex$/.test(request)){ // 剔除具體的包
  4. return callback(null, amd + request); // 通過 amd 定義剔除文件的引用方式
  5. }
  6. callback();
  7. }
  8. ]

不過,更方便的直接使用 webpack-node-externals 來完成。如果項目不是特別複雜,這個配置選項一般用不上。

  • regexp: 使用正則選項,相當於就是通過正則的 test 方法,通過路徑名的檢查,來決定是否剔除。
  1. externals: /^(jquery|$)$/i; //剔除 jquery 和 $ 文件導入
  2. # 剔除如下文件
  3. import $ from jquery;
  4. // or
  5. import $ from $;

target 設定打包環境

這裡,如果項目並不複雜的話,可以直接忽略該屬性設置

因為 webpack 本身都是多環境應用,你可以在 node、web、webworker 甚至其他跨平台環境中:node-webkit、electron-main。其常用選項有三個:

  • node:在後台環境中使用
  • web:在瀏覽器主線程環境中使用
  • webworker:在 web-worker 環境中使用

具體設置方式為:

  1. {
  2. target: node
  3. }

不過,該選項常常用於複雜的打包項目中。比如,一個項目你需要同時輸出在 web 環境中和在 webworker 環境中運行的打包腳本文件。這時候,就涉及到多入口文件的 webpack.config.js 設置。這裡,可以利用 webpack-merge 來幫助我們完成多入口的合併設置。

  1. var baseConfig = {
  2. target: async-node,
  3. entry: {
  4. entry: ./entry.js
  5. },
  6. output: {
  7. filename: [name].js,
  8. path: path.resolve(__dirname, ./dist)
  9. }
  10. };
  11. let targets = [web, webworker, node, async-node, node-webkit, electron-main].map((target) => {
  12. let base = webpackMerge(baseConfig, {
  13. target: target,
  14. output: {
  15. path: path.resolve(__dirname, ./dist/ + target),
  16. filename: [name]. + target + .js
  17. }
  18. });
  19. return base;
  20. });
  21. module.exports = targets;

在導出的就會同時編譯出 web、webworker、node 等目錄,裡面的文件就是對應環境中使用的包。如果想用原生的可以直接導出,數組形式的配置項:

  1. var path = require(path);
  2. var serverConfig = {
  3. target: node,
  4. output: {
  5. path: path.resolve(__dirname, dist),
  6. filename: lib.node.js
  7. }
  8. };
  9. var clientConfig = {
  10. target: web, // <=== can be omitted as default is web
  11. output: {
  12. path: path.resolve(__dirname, dist),
  13. filename: lib.js
  14. }
  15. };
  16. module.exports = [ serverConfig, clientConfig ];

通過,在 webpack 主文件中,具體設置需要使用的配置,即可完成指定環境的編譯操作。

node 提供模擬包

node 指令主要是讓 webpack 將在 node 環境運行的包,提供相關的設置,能夠直接在打包文件中訪問。其策略可以是提供一個樁(空對象),或者直接將現成的包直接打進去。

其基本可選擇的值主要是直接參考 node 的模塊包名:

  1. console
  2. global
  3. process
  4. __filename
  5. __dirname
  6. Buffer
  7. setImmediate

不過,主要用到的還是 Buffer 和 global 兩個配置項。其打包策略選項有 4 個值:

  • true: 直接將該包的 polyfill 直接打進去
  • "mock": 提供空介面。可以訪問對應的介面名,但是沒有任何作用。
  • "empty": 提供一個空對象。這個連介面名都不能訪問
  • false: 啥都沒有。

上述默認配置值為:

  1. node: {
  2. console: false,
  3. global: true,
  4. process: true,
  5. __filename: "mock",
  6. __dirname: "mock",
  7. Buffer: true,
  8. setImmediate: true
  9. }

這樣設置了之後,我們可以直接在主文件入口中訪問:

  1. const Buffer = require(Buffer);
  2. // doSth()

如果你想啥都不提供,最好直接將 node 設置為 false

  1. node:false

devtool 開啟 sourceMap 選項

devtool 主要是給 JS 文件添加對一個的 sourceMap 文件。不過,webpack 可以生成多種 sourceMap 文件,具體的區別是,映射還原度的區別。這裡直接參考 webpack 官方文檔即可。

其中,最常用的選項就是 source-mapcheap-eval-source-map。(這裡實在太多了,各位看著選個合適的就行)如果你想關閉,sourceMap 的話,可以直接使用 hidden-source-map,或者不傳。比如,下面的配置:

  1. if (COMPILE) {
  2. config.devtool = hidden-source-map;
  3. config.output.path = path.join(__dirname,public);
  4. }

devServer 本地 localhost 調試

devServer 是 webpack 提供一個簡便的調試服務。它可以根據欄位配置,自動起一個 server 來進行調試。如果用過 webpack-dev-server 的同學,應該就很熟悉了。那如果利用 webpack,起一個簡單的 server 服務呢?這裡,直接給一份配置即可:

  1. devServer: {
  2. contentBase: path.join(__dirname, "dist"), // 設置靜態資源訪問路徑
  3. compress: true, // 執行壓縮
  4. port: 9000 // 監聽的埠
  5. }

不過,更常用的是利用 webpack server 的熱更新機制。配置列表為:

  1. devServer: {
  2. contentBase: path.join(__dirname, "dist"),
  3. compress: true,
  4. port: 9000,
  5. hot: true,
  6. hotOnly: true
  7. }

接著,你就可以通過 localhost:9000 來訪問你編譯過後的目錄。

剩下 server 配置,大家可以直接參考 webpack devServer。

最後說幾句

到這裡,webpack 3.x 的整個內容差不多已經闡述完了。不過,關於 plugin 的內容,我們這裡並沒有過多提及,因為,對於一般 webpack 純使用者來說,直接使用現有成熟的 plugin 即可。還有一個原因是,webpack plugin 內容真的有點多,這裡篇幅有限就不寫了。後面專門針對其內容詳述一篇。

宣傳宣傳

更多精彩內容,歡迎關注我的公眾號 前端小吉米:


推薦閱讀:

極樂技術周報(第十二期)
在移動端使用transform: translate代替top left marg等做位移有好處么 ?
手淘的Flexible方案能使用雪碧圖嗎?
極樂技術周報(第四十一期)

TAG:前端开发 | webpack |