概念
本質(zhì)上,webpack
是一個現(xiàn)代javascript
應(yīng)用程序的靜態(tài)模塊打包器(module bundler).當webpack
處理應(yīng)用程序時,它會遞歸地構(gòu)建一個依賴關(guān)系圖,其中包含應(yīng)用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個bundle
.
主要有四個核心概念:
- 入口
retry
- 輸出
output
- loader
- 插件
plugins
入口(retry)
入口起點(retry point)指示webpack
應(yīng)該使用哪個模塊來作為構(gòu)建其內(nèi)部依賴圖的開始.進入入口起點后,webpack
會找出有哪些模塊和庫是入口起點(直接和間接)依賴的.每個依賴項隨機被處理,最后輸出到稱之為bundles
的文件中.
- 單個入口語法(字符串形式)
const config = {
entry: './path/to/my/entry/file.js'
};
module.exports = config;
相當于
const config = {
entry: {
main: './path/to/my/entry/file.js'
}
};
當向entry
傳遞一個數(shù)組時,將創(chuàng)建"多個主入口".在需要多個依賴文件一起注入,并且將它們的依賴導向到一個"chunk"時.
- 對象語法
const config = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
};
對象語法是應(yīng)用程序中定義入口的最可拓展的方式.
- 常見場景
分離 應(yīng)用程序(app) 和 第三方庫(vendor) 入口.
這種方式比較常見于只有一個入口起點(不包括vendor)的單頁應(yīng)用程序(spa)中.此設(shè)置允許你使用CommonsChunkPlugin
從[應(yīng)用程序bundle]中提取vendor
引用(vendor reference)到vendor bundle. - 多頁面應(yīng)用程序
const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
這是告訴webpack需要3個獨立分離的依賴圖
輸出(output)
即使可以存在多個入口起點,但只能指定一個輸出配置
- 用法
最低要求是將輸出的值設(shè)置為一個對象,包括以下兩個屬性
filename
用于輸出文件的文件名
目標輸出目錄path
的絕對路徑 - 多個入口起點
如果配置了創(chuàng)建了多個單獨的"chunk",則應(yīng)該使用占位符
來確保每個文件具有唯一的名稱
{
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
- 高級進階
output: {
path: "/home/proj/cdn/assets/[hash]",
publicPath: "http://cdn.example.com/assets/[hash]/"
}
在編譯時不知道最終輸出文件的 publicPath 的情況下,publicPath 可以留空,并且在入口起點文件運行時動態(tài)設(shè)置.如果你在編譯時不知道 publicPath,你可以先忽略它,并且在入口起點設(shè)置__webpack_public_path__
loader
loader用于對模塊的源代碼進行轉(zhuǎn)換.loader可以使你在import
或"加載"模塊時預處理文件.因此,loader類似于其它構(gòu)建工具中的"任務(wù)(task)",并提供了處理前端構(gòu)建步驟的強大方法.
- 使用loader
- 配置(推薦): 在
webpack.config.js
文件中指定loader - 內(nèi)聯(lián): 在每個
import
語句中顯示指定loader - CLI: 在shell命令中指定它們
- 配置(推薦): 在
- 配置[Configuration]
module.rules
荀彧你在webpack設(shè)置中指定多個loader. - 內(nèi)聯(lián)
可以在import
語句或任何等效于"import"的方式中指定loader.使用!
將資源中的loader分開
import Styles from 'style-loader!css-loader?modules!./styles.css';
通過前置所有規(guī)則以及使用!
,可以對應(yīng)覆蓋到配置的任意loader
- CLI
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
這會對 .jade
文件使用 jade-loader
,對 .css
文件使用 style-loader
和 css-loader
- loader特新
- 支持鏈式傳遞,能夠?qū)Y源使用流水線
- 可以是同步的,也可以是異步的
- 運行在node.js中,并且能夠執(zhí)行任何可能的操作
- 接受查詢參數(shù),用于傳遞配置
- 也能夠使用
options
對象進行配置 - 處理使用
package.json
常見的main屬性,可以將普通的npm模塊導出為loader,做法是在package.json
中定義一個loader字段 - 插件可以為loader帶來更多特性
- loader能夠產(chǎn)生額外的任意文件
- 解析loader
loader遵循標準的模塊解析,多數(shù)情況下,loader將從模塊路徑解析.
loader模塊需要導出為一個函數(shù),并且使用Node.js兼容的JavaScript編寫.通常使用npm進行管理,但是也可以將自定義loader作為應(yīng)用程序中的文件
插件(Plugins)
webpack的支柱功能,其自身也是構(gòu)建于你在webpack配置中用到的相同的插件系統(tǒng)之上
插件目的在于解決與loader無法實現(xiàn)的其他事
- 剖析
webpack插件是一個具有apply
屬性的JavaScript對象.apply
屬性會被webpack compiler調(diào)用,并且compiler對象可在整個編譯聲明周期訪問 - 用法
由于插件可以攜帶參數(shù)/選項,你必須在webpack配置中,向plugins
屬性傳入new
實例 - 配置
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
- Node API
即便使用Node API,用戶也應(yīng)該在配置中傳入plugins屬性.webpack compiler并不是推薦的使用方式
some-node-script.js
const webpack = require('webpack'); //訪問 webpack 運行時(runtime)
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
compiler.apply(new webpack.ProgressPlugin());
compiler.run(function(err, stats) {
// ...
});
配置
webpack配置是標準的Node.js CommonJS模塊:
- 通過
require(...)
導入其他文件 - 通過
require(...)
使用npm的工具函數(shù) - 使用JavaScript控制流表達式,例如三元表達式
- 對常用值使用常量或變量
- 編寫并執(zhí)行函數(shù)來生成部分配置
模塊
在模塊化編程中,開發(fā)者將程序分解成離散功能塊,并稱之為模塊
- 什么是webpack模塊
對比Node.js模塊,webpack模塊能夠以各種方式表達它們的依賴關(guān)系- ES2015
import
語句 - CommonJS
require()
語句 - AMD
define
和require
語句 - css/sass/less文件中的
@import
語句 - 樣式(
url(...)
)或html文件(<img src=...>
)中的圖片鏈接
- ES2015
- 支持的模塊類型
webpack通過loader可以支持各種語句和預處理器編寫模塊.loader描述了webpack如何處理 非JavaScript模塊,并且在bundle中引入這些依賴
模塊解析
resolver是一個庫,用于幫助找到模塊的絕對路徑.一個模塊可以作為另一個模塊的依賴模塊,然后被后者引用.當打包模塊時,webpack使用enhanced-resolve
來解析文件路徑
-
webpack中的解析規(guī)則
使用enhanced-resolve
,webpack能夠解析三種文件路徑
1 絕對路徑
2 相對路徑
這種情況下,使用import
或require
的資源文件所在的目錄認為是上下文目錄.在import/require
中給定的相對路徑會添加上下文路徑來產(chǎn)生模塊的絕對路徑
3 模塊路徑
模塊將在resolve.modules
中指定的所有目錄內(nèi)搜索.可以使用resolve.alias
配置選項來創(chuàng)建一個別名.
一旦根據(jù)上述規(guī)則解析路徑后,解析器(resolve)將檢查路徑是否指向文件或目錄.
如果路徑指向一個文件:- 如果路徑具有文件拓展名,則被直接打包
- 否則,將使用
resolve.extensions
選項作為文件拓展名來解析(接受哪些拓展名)
如果路徑指向一個文件夾:
- 如果文件夾包含
package.json
文件,則按照順序查找resolve.mainFiles
配置選項中指定的字段.并且package.json
中的第一個這樣的字段確定文件路徑. - 如果
package.json
文件不存在或者文件中的main字段沒有返回一個有效路徑,則按照順序查找resolve.mainFiles
配置選項中指定的文件名,看是否能在import/require目錄下匹配到一個存在的文件名 - 文件拓展名通過
resolve.extensions
選擇采用類似的方法進行解析
webpack根據(jù)構(gòu)建目標為這些選項提供了合理的默認配置
構(gòu)建目標
因為服務(wù)器和瀏覽器代碼都可以使用JavaScript編寫,所以webpack提供了多種構(gòu)建目標.
- 用法
只需要在webpack配置中設(shè)置target的值
module.exports = {
target: 'node'
};
這個例子中,使用node
webpack會編譯為用于[類 Node.js]環(huán)境(使用Node.js的require
,而不是使用任意內(nèi)置模塊(如fs
或path
)來加載chunk)
- 多個Target
盡管webpack不支持向target
傳入多個字符串,但是可以通過打包兩份分離的配置來創(chuàng)建同構(gòu)的庫
var path = require('path');
var serverConfig = {
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.node.js'
}
//…
};
var clientConfig = {
target: 'web', // <=== 默認是 'web'奄容,可省略
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.js'
}
//…
};
module.exports = [ serverConfig, clientConfig ];
這個例子會在dist文件夾下創(chuàng)建lib.js
和lib.node.js
文件
Manifest
使用webpack構(gòu)建的典型應(yīng)用程序或站點中,有三種主要的代碼類型
- 編寫的源碼
- 源碼依賴的任何第三方的library或vendor代碼
- webpack的runtime和manifest,管理所有模塊的交互
- Runtime
包含:在模塊交互時,連接模塊所需的加載和解析邏輯.包括瀏覽器中的已加載模塊的連接以及懶加載模塊的執(zhí)行邏輯 - Manifest
當編譯器開始執(zhí)行,解析和映射應(yīng)用程序時,它會保留所有模塊的詳細要點,這個數(shù)據(jù)集合成為"Manifest",當完成打包發(fā)送到瀏覽器時,會在運行時通過Manifest來解析和加載模塊.無論選擇哪種模塊語法,那些語句現(xiàn)在都已經(jīng)轉(zhuǎn)換為__webpack_require__
方法,此方法指向模塊標識符.通過使用manifest中的數(shù)據(jù),runtime將能夠查詢模塊標識符,檢索出背后對應(yīng)的模塊.
模塊熱替換
在應(yīng)用程序運行過程中替換,添加或刪除模塊,而無需重新加載整個頁面.主要通過以下幾種方式:
- 保留在完全重新加載頁面時丟失的應(yīng)用程序狀態(tài)
- 只更新變更內(nèi)容,以節(jié)省寶貴的開發(fā)時間
- 調(diào)整樣式更加快速