寫這篇文章是爲了讓本身在自學 webpack
的過程當中有所產出,因而邊讀 webpack 中文文檔 邊寫下了這篇文章,裏面的不少實例都是直接挪用的文檔中的實例,但在一些概念的理解上我加入了本身的想法「未必精確」,因此讀的時候要抱着「懷疑的態度」。javascript
文章內容不只僅是簡單的「概念堆疊」,還有一些「重點」概念的「深刻理解」,不過篇幅有限我不但願這篇文章變成一份冗長的僞文檔,因此所有的內容都是圍繞 webpack
的 4個 核心概念延展開來的,每一個配置後面我都會盡可能跟上一個實例以更加形象的展現配置的具體做用。css
站在個人角度上,讀完這篇文章並不能讓你精通 webpack 可是理解 webpack 中的重要概念,本身編寫一個 webpack.config.js 配置文件仍是能夠的。html
本質上,webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(static module bundler)。在 webpack 處理應用程序時,它會在內部建立一個依賴圖(dependency graph),用於映射到項目須要的每一個模塊,而後將全部這些依賴生成到一個或多個bundle。前端
來自 webpack 中文文檔vue
目前都是使用一些成熟的 CLI
工具,通常都內置 webpack
因此我對 webpack
的認知一直比較少,只是大概的瞭解它是用來管理項目中的 .js
文件依賴,而後打包整個項目的。java
對應屬性:entry
默認值:./src/index.js
node
做用說明: 用來規定 webpack
應該使用哪一個模塊做爲構建內部依賴圖的起點。 webpack
會找出全部「入口模塊」(直接或間接)依賴的「模塊」和 [library]。webpack
代碼示例:git
// weboack.config.js
module.exports = {
entry: './path/to/entry/file.js'
}
複製代碼
對應屬性:output
主輸出文件默路徑:./dist/main.js
其餘文件默認路徑:./dist/<filename>
github
做用說明: 用來規定 webpack
在那裏輸出 bundles
以及如何命名這些文件。
// weboack.config.js
const path = require('path') // Node.js 核心模塊,用於操做文件路徑
代碼示例:
module.exports = {
entry: './path/to/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '<WhateverYouLike>.js'
}
}
複製代碼
對應屬性:module->rules
做用說明: 做爲開箱即用的自帶特性,webpack
自身只支持處理 JavaScript
文件。而 loader
可以讓 webpack
處理那些非 JavaScript
文件,而且先將它們轉換爲有效「模塊」,而後添加到「依賴圖」中,提供給應用程序使用。
屬性特徵:
test
: 利用「正則表達式」規定 loader
用於哪些或哪一個文件。use
: 規定運行時使用哪一個 loader
。代碼示例:
// webpack.config.js
const path = require('path')
module.exports = {
...
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
}
複製代碼
代碼做用: 當運行包含 .txt 文件的 require() 或 import 語句時,在它打包以前,先使用 raw-loader 轉換。
對應屬性:plugings
做用說明: 打包優化、資源管理和注入環境變量。
代碼實例:
// webpack.config.js
const HtmlWebpackPlugn = require('html-webpack-plugin') // 提早經過 npm 安裝
const webpack = require('webpack') //用於訪問內置插件
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
}
複製代碼
// webpack.config.js
module.exports = {
entry: {
main: './path/to/entry/file.js'
}
}
// 可簡寫爲以下形式
module.exports = {
enrty: './path/to/enrty/file.js'
}
/* * 當你須要爲只有一個入口的應用程序或工具(library)快速設置 webpack 配置時, * 簡寫會是個很不錯的選擇。然而,使用此語法在擴展配置時有失靈活性。 */
複製代碼
思考:當你向 entry
傳入一個數組時會發生什麼? 解釋:向 entry
傳入「文件路徑數組」將建立「多個主入口」。在你想要多個依賴文件一塊兒注入,而且將它們的依賴導向到一個 chunk
時,傳入數組的方式就頗有用。
用法:entry: {<enrtyChunkName: String>: <Path: String | Array>}
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
}
// 對象語法會比較繁瑣。然而,這是應用程序中定義入口的最可擴展的方式。
複製代碼
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js'
vendors: './src/vendors.js'
}
}
/* * webpack 從 app.js 和 vendors.js 開始建立依賴圖。 * 這些依賴圖是彼此徹底分離、互相獨立的(每一個 bundle 中都有一個 webpack 引導)。 * 這種方式比較常見於,只有一個入口起點(不包括 vendor)的單頁應用程序中。 * * 此設置容許你使用 CommonsChunkPlugin 從應用程序依賴圖中提取 vendor 到 vendor 依賴圖,並把引用 vendor 的部分替換爲 __webpack_require__() 調用。 * 若是應用程序依賴圖中沒有 vendor 代碼,那麼你能夠在 webpack 中實現被稱爲長效緩存的通用模式。 * 說實話,目前看不懂上面這段話,因此也不曉得怎麼通俗的表述。 */
複製代碼
// webpack.config.js
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
}
/* * webpack 分離 3 個的依賴圖 * * 在多頁應用中,每當頁面跳轉時服務器將爲你獲取一個新的 HTML 文檔。 * 頁面從新加載新文檔,而且資源被從新下載。這給了咱們特殊的機會去作不少事: * 使用 CommonsChunkPlugin 使全部頁面的應用程序共享代碼建立依賴圖, * 入口增多,多頁應用可以複用不一樣入口的大量重複代碼/模塊。 */
複製代碼
注意,即便能夠存在多個入口,但只配置一個出口設置。
在 webpack
中配置 output
的最低要求是,將它的值是一個包括如下兩點的對象:
filename
: 輸出文件的文件名。path
: 輸出目錄的絕對路徑。// webpack.config.js
module.exports = {
output: {
filename: '<WhateverYouLike>.js',
path: '/path/to/project'
}
}
// 此配置將一個單獨的 .js 文件輸出到 /path/to/project 目錄中。
複製代碼
若是配置建立了多個單獨的入口,則應該使用 佔位符 來確保每一個文件具備惟一的名稱。
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
// 寫入到硬盤:./dist/app.js, ./dist/search.js
複製代碼
內部ID:[id]
入口名稱:[name]
基於構建的hash(每次構建都會改變):[hash]
基於內容的hash(文件內容改變纔會改變):[chunkhash]
官網所謂高級進階其實就是利用哈希佔位符構建隨版本迭代的文件命名方式這裏不展現了。
比較有用的是如何動態設置 publicPath
:
output.publicPath
: 全部資源的基礎路徑,它被稱爲公共路徑,以 /
結束,示例:// webpack.config.js
module.exports = {
...
output: {
publicPath: '/assets/',
chunkFilename: '[id].chunk.js'
}
};
/* * HTML loader 輸入出:<link href="/assets/spinner.gif" /> * CSS:background-image: url(/assets/spinner.gif); * 靜態資源最終訪問路徑 = output.publicPath + loader 或插件等配置路徑 */
複製代碼
devServer.publicPath
: 肯定從哪裏提供 bundle假設服務器運行在 http://localhost:8080
而且 output.filename
被設置爲 bundle.js
。默認 publicPath
是 /
,因此你的包能夠經過 http://localhost:8080/bundle.js
訪問。
能夠修改經過 devServer.publicPath
來修改請求資源時的服務器前綴,示例:
// webpack.config.js
module.exports = {
...
devServer: {
publicPath: '/assets/'
}
};
/* * 如今能夠經過 http://localhost:8080/assets/bundle.js 訪問 bundle。 * 確保 publicPath 老是以斜槓(/)開頭和結尾。 * devServer.publicPath 也能夠是一個完整的 URL。 * 通常狀況下都要保證 devServer.publicPath 與 output.publicPath 保持一致。 */
複製代碼
devServer.contentBase
: 告訴服務器從哪裏提供內容,只有在提供靜態文件時才須要默認狀況下,將使用當前工做目錄做爲提供內容的目錄,可是你能夠修改成其餘目錄,示例:
// webpack.config.js
module.exports = {
...
devServer: {
// 推薦使用絕對路徑。
contentBase: path.join(__dirname, 'public')
}
};
// 也能夠從多個目錄提供內容
module.exports = {
...
devServer: {
contentBase: [path.join(__dirname, 'public'), path.join(__dirname, 'assets')]
}
};
// 具體做用不詳,官網並無給出說明也懶得查了
複製代碼
// webpack.config.js
...
const BASE_URL = process.env.NODE_ENV === 'production'
? '/'
: '/'
module.exports = {
...
publicPath: BASE_URL,
...
}
// 方法來自 iview-admin vue.config.js
// 我不知道我理解的動態設置對不對,不過官網給的 __webpack_public_path__ 我沒看明白
複製代碼
loader
用於對模塊的源代碼進行轉換,可使你在「載入」模塊時預處理文件。
loader
相似於其餘構建工具中「任務(task
)」,提供了處理前端構建步驟的方法。
loader
能夠將文件從不一樣的語言(如 TypeScript
)轉換爲 JavaScript
,或將內聯圖像轉換爲 data URL
。容許你直接在 JavaScript
模塊中 import CSS
文件。
配置 loader
使 webpack
加載 CSS
文件,或者將 TypeScript
轉爲 JavaScript
。
首先安裝相對應的 loader
:
npm install --save-dev css-loader
npm install --save-dev ts-loader
複製代碼
而後配置 webpack
對每一個 .css
使用 css-loader
,全部 .ts
文件使用 ts-loader
:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
}
複製代碼
webpack.config.js
文件中指定 loader
。(推薦)前面展現過了,這裏就不重複了。
import
語句中顯式指定 loader
。能夠在 import
語句或任何等效於 import
的方式中指定 loader
。使用 !
將資源中的 loader
分開。分開的每一個部分都相對於當前目錄解析,示例:
import Styles from 'style-loader!css-loader?modules!./styles.css';
複製代碼
shell
命令指定 loader
。webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
複製代碼
loader
支持鏈式傳遞。loader
鏈中每一個 loader
,都對前一個 loader
處理後的資源進行轉換。loader
鏈會按照相反的順序執行。第一個 loader
將(應用轉換後的資源做爲)返回結果傳遞給下一個 loader
,依次這樣執行下去。最終,在鏈中最後一個 loader
,返回 webpack
所預期的 JavaScript
。loader
能夠是同步的,也能夠是異步的。loader
運行在 Node.js
中,而且可以執行任何可能的操做。loader
接收查詢參數,用於對 loader
傳遞配置。loader
也可以使用 options 對象進行配置。package.json
常見的 main
屬性,還能夠將普通的 npm
模塊導出爲 loader
,作法是在 package.json
裏定義一個 loader
字段。loader
帶來更多特性。loader
可以產生額外的任意文件。loader
遵循標準的 模塊解析。多數狀況下,loader
將從模塊路徑(一般將模塊路徑認爲是 node_modules
)解析。
loader
模塊須要導出爲一個函數,而且使用 Node.js
兼容的 JavaScript
編寫。一般使用 npm
進行管理,可是也能夠將自定義 loader
做爲應用程序中的文件。按照約定,loader
一般被命名爲 xxx-loader
(例如 json-loader
)。有關詳細信息,請查看 如何編寫 loader?。
插件是 webpack
的支柱功能。webpack
自身也構建於插件系統之上。
插件目的在於解決 loader
沒法實現的其餘事。
webpack
插件是一個具備 apply
方法的 JavaScript
對象。apply
屬性會被 webpack compiler
調用,而且 compiler
對象可在整個編譯生命週期訪問。
// ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
// compiler hook 的 tap 方法的第一個參數,應該是駝峯式命名的插件名稱。
// 建議爲此使用一個常量,以便它能夠在全部 hook 中複用。
compiler.hooks.run.tap(pluginName, compilation => {
console.log('webpack 構建過程開始!');
});
}
}
// 插件編寫屬於比較深刻的內容,這裏不過多探討,目前僅須要知道實現原理便可
複製代碼
因爲插件能夠攜帶參數/選項,你必須在 webpack
配置中,向 plugins
屬性傳入 new
實例。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin') //經過 npm 安裝
const webpack = require('webpack') //訪問內置的插件
const path = require('path')
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
}
複製代碼
對應屬性:mode | String
做用說明: 經過將 mode
參數設置爲 development
, production
或 none
,能夠啓用對應環境下 webpack
內置的優化。默認值爲 production
。
// webpack.config.js
module.exports = {
...
mode: 'production'
};
複製代碼
webpack --mode=production
複製代碼
選項 | 描述 |
---|---|
development | 會將 process.env.NODE_ENV 的值設爲 development。啓用 NamedChunksPlugin 和 NamedModulesPlugin。 |
production | 會將 process.env.NODE_ENV 的值設爲 production。啓用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin. |
None | 不選用任何默認優化選項 |
// webpack.config.js
var config = {
entry: './app.js'
...
}
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
...
}
return config
}
複製代碼
在模塊化編程中,開發者將程序分解成離散功能塊,並稱之爲「模塊」。
每一個模塊具備比完整程序更小的接觸面,使得校驗、調試、測試垂手可得。 精心編寫的「模塊」提供了可靠的抽象和封裝界限,使得應用程序中每一個模塊都具備條理清楚的設計和明確的目的。
webpack
將「模塊」的概念應用於項目中的任何文件。
對比 Node.js
模塊,webpack
「模塊」可以以各類方式表達它們的依賴關係,幾個例子以下:
樣式:(url(...))
ES2015
: import
CommonJS
: require()
HTML
: <img src=...>
AMD
: define | require
css/sass/less
: @import
webpack
經過 loader
能夠支持各類語言和預處理器編寫模塊。loader
描述了 webpack
如何處理「非 JavaScript(non-JavaScript) 模塊」,而且在 bundle
中引入這些「依賴」。
目前 webpack
已經但不限於支持如下語言的 loader
:
resolver
是一個庫,用於幫助找到模塊的絕對路徑。 它幫助 webpack
從每一個如 require/import
語句中,找到須要引入到 bundle
中的模塊代碼。 當打包模塊時,webpack
使用 enhanced-resolve
來解析文件路徑。
使用 enhanced-resolve
,webpack
可以解析三種文件路徑:
// 已經取得文件的絕對路徑,所以不須要進一步再作解析。
import '/home/me/file';
import 'C:\\Users\\me\\file';
複製代碼
// 在這種狀況下,使用 import 或 require 的資源文件所在的目錄,被認爲是上下文目錄。
// 在 import/require 中給定的相對路徑,會拼接此上下文路徑,以產生模塊的絕對路徑。
import '../src/file1';
import './file2';
複製代碼
import 'module';
import 'module/lib/file';
// 解釋很囉嗦,感興趣能夠本身去看一下文檔
複製代碼
每次文件系統訪問都會被緩存,以便更快觸發對同一文件的多個並行或串行請求。在 觀察模式下,只有修改過的文件會從緩存中摘出。若是關閉觀察模式,會在每次編譯前清理緩存。
任什麼時候候,一個文件依賴於另外一個文件,webpack
就把此視爲文件之間有「依賴關係」。這使得 webpack
能夠接收非代碼資源(例如 images
或 web fonts
),而且能夠把它們做爲「依賴」提供給你的應用程序。
webpack
從命令行或配置文件中定義的「入口」開始,遞歸地構建一個依賴圖,這個依賴圖包含着應用程序所需的每一個模塊,而後將全部這些模塊打包爲少許可由瀏覽器加載的 bundle
(一般只有一個)。
webpack
支持全部 ES5
兼容(IE8
及如下不提供支持)的瀏覽器。webpack
的 import()
和 require.ensure()
須要環境中有 Promise
。若是你想要支持舊版本瀏覽器,你應該在使用這些 webpack
提供的表達式以前,先 加載一個 polyfill。
經過整理這篇文檔我已經對 webpack
有了一個初步的認識和了解了。
固然若是你要真正的在項目中投入使用 webpack
僅僅閱讀這一篇文章是不夠的,你還須要去深刻地閱讀了解文檔裏的各類配置參數和其餘經常使用的前端構建工具或預處理器配合 webpack
進行調試使用。
前路漫漫,與君共勉。