webpack進階之路二、(配置)
- Entry:配置模組的入口。
- Output:配置如何輸出最終想要的程式碼。
- Module:配置處理模組的規則。
- Resolve:配置尋找模組的規則。
- Plugins:配置擴充套件外掛
- DevServer:配置DevServer。
- 其他配置項:其他零散的配置項。
- 整體配置解構:整體地描述各配置項的結構。
- 多種配置型別:配置檔案不止可以返回一個Object,還可以返回其他形式。
- 配置總結:尋找配置Webpack的規律,減少思維負擔。
一、Entry
entry是配置模組的入口,可抽象成輸入,Webpack執行構建的第一步將從 入口開始,搜尋及遞迴解析出所有入口依賴的模組。(必填)
1、context
Webpack在尋找相對路徑的檔案時會以context為根目錄,context預設為執行啟動Webpack時所在的當前工作目錄。如果想改變context的預設配置,則可以在配置檔案時設定:
module.exports = {
context: path.resolve(__dirname, 'app')
}
注意,context必須是一個絕對路徑的字串,除此之外,還可以通過在啟動Webpack時帶上引數webpack --context來設定context。
Entry的路徑及其依賴的模組的路徑可能採用相對於context的路徑來描述,context會影響到這些相對路徑所指向的真實檔案。
2、Entry型別
- string ‘./app/entry’ 入口模組的檔案路徑,可以是相對路徑
- array [’./app/entry1’, ‘./app/entry2’] 入口模組的檔案路徑,可以是相對路徑
- object {a: ‘./app/entry-a’, b: [’./app/entry-b1’, ‘./app/entry-b2’]} 配置多個入口,每個入口生成一個Chunk
注意:如果是array型別,則搭配output.library配置項使用時,只有數組裡的最後一個入口檔案的模組會被匯出。
3、Chunk的名稱
- 如果entry是一個string或array,就只會生成一個Chunk,這時Chunk的名稱是main。
- 如果entry是一個object,就可能會出現多個Chunk,這時Chunk的名稱是object鍵值對中鍵的名稱。
4、配置動態Entry
假如專案裡有多個頁面需要為每個頁面的入口配置一個Entry,但這些頁面的數量可能不斷增長,則這時Entry的配置會受到其他因素的影響,導致不能寫成靜態的值。其解決方法是將Entry設定成一個函式動態地返回上面所說的配置,如:
// 同步函式
entry: () => {
return {
a: './pages/a',
b: './pages/b',
}
};
// 非同步函式
entry: () => {
return new Promise((resolve) => {
resolve({
a: './pages/a',
b: './pages/b',
})
})
}
二、Output
output配置如何輸出最終想要的程式碼。
1、filename
output.filename配置輸出檔案的名稱,為string型別。如果只有一個輸出檔案,則可以將它寫成靜態不變的:filename: 'bundle.js'
,但是在有很多Chunk要輸出時,就需要藉助模板和變數,之前所說,Webpack會為每個Chunk取一個名稱,所以我們可以根據Chunk的名稱來區分輸出的檔名:filename: '[name].js'
程式碼裡的[name]代表內建的name變數去替換[name],這時我們可以將它看作一個字串模組函式,每個要輸出的Chunk都會通過這個函式去憑藉輸出的檔名稱。內建變數除了包括name,還包括如下:
- id Chunk的唯一標識,從0開始
- name Chunk的名稱
- hash Chunk的唯一標識的Hash值
- chunkhash Chunk內容的Hash值
其中,hash和chunkhash的長度是可指定的,[hash:8]代表取8位Hash值,預設是20位。
2、chunkFilename
output.chunkFilename配置無入口的Chunk在輸出時的檔名稱。chunkFilename和上面的filename非常類似,但chunkFilename只用於指定在執行過程中生成的Chunk在輸出時的檔名稱。會在執行時生成Chunk的常見場景包括:使用CommonChunkPlugin、使用import(‘path/to/module’)動態載入等。chunkFilename支援和filename一致的內建變數。
3、path
output.path配置輸出檔案存在本地目錄,必須是string型別的絕對路徑。通常通過Node.js的path模組去獲取絕對路徑:
path: path.resolve(__dirname, 'dist_[hash]')
4、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.path和output.publicPath都支援字串模板,內建變數只有一個,即hash,代表一次編譯操作的Hash值。
5、crossOriginLoading
Webpack輸出的部分程式碼塊可能需要非同步載入,而非同步載入時通過JSONP方式實現的。JSONP的原理是動態地向HTML中插入一個<script src="url"></script>
標籤去載入非同步資源。output.crossOriginLoading則是用於配置這個非同步插入的標籤的crossorigin值。
script標籤的crossorigin屬性可以取以下值:
- anonymous(預設),在載入此指令碼資源時不會帶上使用者的Cookies。
- use-credentials,在載入此指令碼資源時會帶上使用者的Cookies。
通常用設定crossorigin來獲取非同步載入的指令碼執行時的詳細錯誤資訊。
6、libraryTarget和library
當Webpack去構建一個可以被其他模組匯入使用的庫時,需要用到libraryTarget和library。
- output.libraryTarget配置以何種方式匯出庫。
- output.library配置匯出庫的名稱。
它們通常搭配在一起使用
output.libraryTarget是字串的列舉型別,支援以下配置。- var(預設)
編寫的庫將通過var被賦值給通過library指定名稱的變數。
假如配置了output.library=‘LibraryName’,則輸出和使用的程式碼如下:
假如output.library為空,則直接輸出:// Webpack輸出的程式碼 var LibraryName = lb_code; // 使用庫的方法 LibraryName.doSomething();
lib_code
其中,lib_code是指匯出庫的程式碼內容,是有返回值的一個自執行函式。 - commonjs
編寫庫將通過CommonJS規範匯出。
假如配置了output.library=“LibraryName”,則輸出和使用的程式碼如下:
其中,library-name-in-npm是指模組被髮布到npm程式碼倉庫時的名稱。// Webpack輸出的程式碼 exports['LibraryName'] = lib_code; // 使用庫的方法 require('library-name-in-npm')['LibraryName'].doSomeThing();
- var(預設)
- commonjs2
編寫的庫將通過CommonJS2規範匯出,輸出和使用的程式碼如下:
CommonJS2和CommonJS規範相似,差別在於CommonJS只能用exports匯出,CommonJS2在CommonJS的基礎上增加了module.exports的匯出方式。// Webpack輸出的程式碼 module.exports = lib_code; // 使用庫的方法 require('library-name-in-npm').doSomething();
- this
編寫的庫將通過this被賦值給通過library指定的名稱,輸出和使用的程式碼如下:// Webpack輸出的程式碼 this['LibraryName'] = lib_code; // 使用庫的方法 this.LibraryName.doSomething();
- window
編寫的庫將通過window賦值給通過library指定名稱,輸出和使用的程式碼如下:// Webpack輸出的程式碼 window['LibraryName'] = lib_code; // 使用庫的方法 window.LibraryName.doSomething();
- this
7、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;
以上只是output中的常用配置項。
三、Module
module配置處理模組的規則
1.配置Loader
rules配置模組的讀取和解析規則,通常用來配置Loader。其型別是一個數組,數組裡的每一個都描述瞭如何處理部分檔案。配置一項rules時大致可通過以下方式來完成。
- 條件匹配:通過test、include、exclude三個配置項來選中Loader要應用規則的檔案。
- 應用規則:對選中的檔案通過use配置項來應用Loader,可以只應用一個Loader或者按照從後往前的順序應用一組Loader,同時可以分別向Loader傳入引數。
- 重置順序:一組Loader的執行順序預設是從右到左執行了,通過enforce選項可以將其中一個Loader的執行順序放到最前或者最後。
例如:
module: {
rules: [
{
// 命中JavaScript檔案
test: /\.js$/,
// 用babel-loader轉換JavaScript檔案
// ?cancheDirectory表示傳給babel-loader的引數,用於快取babel的編譯結果加快重新編譯的速度
use: ['babel-loader?cacheDirectory'],
// 只命中src目錄裡的JavaScript檔案,加快Webpack的搜尋速度
include: path.resolve(__dirname, 'src')
},
{
// 命中SCSS檔案
test: /\.scss$/,
// 使用一組Loader去處理SCSS檔案
// 處理順序為從後到前,即先交給sass-loader處理,再將結果交給css-loader,最後交給style-loader
user: ['style-loader', 'scc-loader', 'sass-loader'],
// 排除node_modules目錄下的檔案
exclude: path.resolve(__dirname, 'node_module'),
},
{
// 對非文字檔案採用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'),
]
數組裡的每項之間是“或”的關係,即檔案的路徑只要滿足陣列中的任何一個條件,就會被命中。
2、noParse
noParse配置項可以讓Webpack忽略對部分沒蔡總模組化的檔案的遞迴解析和處理,這樣做的好處是能提高構建效能。原因是一些庫如jQuery、ChartJS龐大又沒有采用模組化標準,讓Webpack去解析這些檔案既耗時又沒有意義。
noParse是可選的配置項,型別需要RegExp、[RegExp]、function中的一種。
例如,若想要huluejQuery、ChartJS,則可以使用如下程式碼:
// 使用正則表示式
noParse: /jquery | chartjs/
// 使用函式,從Webpack 3.0.0 開始支援
noParse: (content) => {
// content代表一個模組的檔案路徑
// 返回true或false
return /jquery | chartjs/.test(content);
}
注意,被忽略的檔案裡不應該包含import、require、define等模組化語句,不然會導致在構建出的程式碼中包含無法再瀏覽器環境下執行的模組化語句。
3、parser
因為Webpack是以模組化的JavaScript檔案為入口的,所以內建了對模組化JavaScript的解析功能,支援AMD、CommonJS、SystemJS、ES6。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
}
}
]
}
4、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的兩份程式碼的位置寫在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']
Webpack會按照數組裡的順序在package.json檔案裡尋找,只會使用找到的第一個檔案。
假如我們想優先採用ES6的那份程式碼,則可以這樣配置:
mainFields: ['jsnext: main', 'browser', 'main']
-
extensions
在匯入語句沒帶檔案字尾時,Webpack會自動帶上字尾後去嘗試訪問檔案是否存在。resolve.extensions用於配置在嘗試過程中用到的字尾列表,預設是:
extensions
: [’.js’, ‘.json’]
也就是說,當遇到require(’./data’)這樣的匯入語句時,Webpack會先尋找./data.js檔案,如果該檔案不存在,就去尋找./data.json檔案,如果還是找不到,就報錯。
假如我們想讓Webpack優先使用目錄下的TypeScript檔案,則可以這樣配置:
extensions
: [’.ts’, ‘.js’, ‘.json’] -
modules
resolve.module配置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
enforceModuleExtension和enforceExtension的作用類似,但enforceModuleExtension只對node_modules下的模組生效。enforceModuleExtension通常搭配enforceExtension使用,在enforceExtension: true時,因為安裝第三方模組中大多數匯入語句都沒帶檔案的字尾,所以這時通過配置enforceModuleExtension: false來相容第三方模組。
5、Plugin
Plugin用於擴充套件Webpack的功能,各種各樣的Plugin幾乎可以讓Webpack做任何與構建相關的事情。
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。
6、DevServer
使用DevServer可以提高開發效率,它提供了一些配置項可以用於改變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來實現實時預覽,但這時我們需要去http://localhost:8080/webpack-dev-server/實時預覽自己的網頁。
如果想使用DevServer的模組熱替換機制去實現實時預覽,則最方便的方法是直接開啟inline。
-
historyApiFallback: true
這會導致任何請求都會返回index.html檔案,這隻能用於只有一個HTML檔案的應用。
如果我們的應用由多個單頁應用組成,則需要DevServer根據不同的請求返回不同的HTML檔案,配置如下:historyApiFallback: { // 使用正則匹配命中路由 rewrite: [ // /user開頭的都返回user.html { from: /^\user/, to: '/user.html' }, { from: /^\/game/, to: '/game.html' }, // 其他的都返回index.html { from: /./, to: 'index.html' }, ] }
-
contentBase
devServer.contentBase配置DevServerHTTP伺服器的檔案根目錄。在預設情況下為當前的執行目錄,通常是專案根目錄,所以在一般情況下不必設定它,除非有額外的檔案需要被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.0
。 host的預設值是127.0.0.1,即只有本地可以訪問DevServer的HTTP服務。 -
port
devServer.host配置項用於配置DevServer服務監聽埠,預設使用8080埠,如果8080埠已經被其他程式佔用,就是用8081。 -
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/ro/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的網頁。
7、其他配置項
除了前面介紹到的配置項,Webpack還提供了一些零散的配置項。下面介紹這些配置項中常用部分。
-
Target
由於JavaScript的應用場景越來越多,從瀏覽器到Node.js,這些執行在不同環境中的JavaScript程式碼存在一些差異。target配置項可以讓webpack構建出針對不同執行環境的程式碼。target可以是如下所示:target值 描述 web 針對瀏覽器(預設),所有程式碼都集中在一個檔案裡 node 針對Node.js,使用require語句載入Chunk程式碼 async-node 針對Node.js,使用非同步載入Chunk程式碼 webworker 針對WebWorker 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 = { devtool: 'source-map' }
在使用DevServer時,監聽預設預設是開啟的。
除此之外,Webpack還提供了watchOptions配置項去更靈活的控制監聽模式,使用如下:
module.export = { // 只有在開啟監聽模式時,watchOptions才有意義 // 預設為false,也就是不開啟 watch: true, // 監聽模式執行時的引數 // 在開啟監聽模式時,才有意義 watchOptions: { // 不監聽的檔案或資料夾,支援正則匹配 // 預設為空 ignored: /node_modules/, // 監聽到變化後會等300ms再去執行動作,防止檔案更新太快導致重新編譯頻率太高 // 預設為300ms aggregateTimeout: 300, // 判斷檔案是否發生變化是通過不停的詢問系統指定檔案有沒有變化的實現的 // 預設每秒詢問1000次 poll: 1000 } }
-
Externals
Externals用來告訴在Webpack要構建的程式碼中使用了哪些不被打包的模組,也就是說這些模組是外部環境提供的,Webpack在打包時可以忽略它們。
有些JavaScript執行環境可能內建了一些全域性變數或者模組,例如在我們的HTML HEAD標籤裡通過以下程式碼引入jQuery:
<script src="path/to/jquery.js"></script>
這時,全域性變數jQuery就會被注入網頁的JavaScript執行環境裡。
如果想在使用模組化的原始碼裡匯入和使用jQuery,則可能需要這樣:import $ from 'jquery'; $('.my-element');
構建後我們會發現輸出的Chunk裡包含的jQuery庫的內容,這導致jQuery庫出現了兩次,浪費,浪費載入流量,最好是Chunk裡不包含jQuery庫的內容。
Externals配置項就是用於解決這個問題的。
通過externals可以告訴Webpack在JavaScript執行環境中已經內建了哪些全域性變數,不用將這些全域性變數打包到程式碼中而是直接使用它們。要解決以上問題,可以這樣配置externals:module.export = { externals: { // 將匯入語句裡的jquery替換成執行環境裡的全域性變數jQuery jquery: 'jQuery' } }
-
ResolveLoader
ResolveLoader用來告訴Webpack如何去尋找Loader,因為在使用Loader時是通過其包名稱去處理原始檔。
ResolveLoader的預設配置如下:module.exports = { resolveLoader: { // 去哪個目錄下尋找Loader modules: ['node_modules'], // 入口檔案的字尾 extensions: ['.js', '.json'], // 指明入口檔案位置的欄位 mainFields: ['loader', 'main'] } }
該配置項常用於載入本地的Loader。
8. 整體配置結構
基礎整體結構:
const path = require('path');
module.exports = {
// entry表示入口,Webpack執行構建的第一步將從Entry開始,可抽象成輸入
// 型別可以是string、object、array
entry: './app/entry', // 只有1個入口,入口只有1個檔案
entry: ['./app/entry1', './app/entry2'], // 只有1個入口,入口有兩個檔案
entry: { // 有兩個入口
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_module為根目錄
'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
}
}
9、多種配置型別
除了通過匯出一個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(ifProduction){ plugins.push( // 壓縮輸出的JavaScript程式碼 new UglifyJsPlugin() ) } return { plufins: plufins, // 在生成環境中不輸出Source Map devtool: isProduction ? undefined : 'source-map', } }
在執行Webpack時,會向這個函式傳入兩個引數,如下所述。
- env: 當前執行時的Webpack專屬環境變數,env時一個Object。讀取時直接訪問Object的屬性,將它設定為需要在啟動Webpack時帶上引數。例如啟動命令是
webpack --env.production --env,bao=foo
,則env的值是{"production": "true", "bao": "foo"}
。 - argv: 代表在啟動Webpack時通過命令列傳入的所有的引數,例如
--config、--env、--devtool
,可以通過webpack -h
列出所有Webpack支援的命令列引數。
就以上配置檔案而言,在開發時執行命令webpack構建出方便除錯的程式碼,在需要構建出釋出到線上的程式碼時執行webpack --env.production
構建出壓縮的程式碼。
- env: 當前執行時的Webpack專屬環境變數,env時一個Object。讀取時直接訪問Object的屬性,將它設定為需要在啟動Webpack時帶上引數。例如啟動命令是
-
匯出一個返回Promise函式
在某些情況下不能以同步的方式返回一個描述配置的Object,Webpack還支援匯出一個返回Promise的函式,使用如下:
module.exports = function(env = { }, argv){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
// ...
})
}, 5000)
})
}
- 匯出多份配置
除了匯出一份配置,Webpack還支援匯出一個數組,陣列中可以包含每份配置且每份配置都會執行一遍構建。
注意,Webpack從3.1.0版本才開始支援該特性。
使用如下:
以上配置會導致Webpack針對這三份配置執行三次不同的構建。module.export = [ // 採用Object描述一份配置 { // ... }, // 採用函式描述的一份配置 function(){ return { // ... } }, // 採用非同步函式描述的一份配置 function(){ return Promise(); } ]
這特別適合用Webpack構建一個要上傳到Npm倉庫的庫,因為庫中可能需要包含多種模組化格式的程式碼,例如CommonJS、UMD。
10、總結
從前面的配置看有很多選項,Webpack內建了很多功能,我們不必都記住它們,只需要大概明白Webpack原理和核心概念,並判斷選項大致屬於哪個大模組下,再去查詳細的使用文件即可。
通常我們可用如下經驗去判斷如何配置Webpack:
- 若想讓原始檔加入構建流程中被Webpack控制,則配置entry;
- 若想自定義輸出檔案的位置和名稱,則配置output;
- 若想自定義尋找依賴模組時的策略,則配置resolve;
- 若想自定義解析和轉換檔案的策略,則配置module,通常是配置module.rules裡的Loader;
- 若其他大部分需求可能通過Plugin去實現,則配置plugin。
此篇為本人Webpack學習筆記,來源《深入淺出Webpack》
相關推薦
webpack進階之路二、(配置)
Entry:配置模組的入口。 Output:配置如何輸出最終想要的程式碼。 Module:配置處理模組的規則。 Resolve:配置尋找模組的規則。 Plugins:配置擴充套件外掛 DevServer:配置DevServer。 其他配置項:其他零散的配置項。
webpack進階之路三、(實戰一,使用ES6、ts、Flow、SCSS)
一、使用新語言來開發專案 1、使用ES6語言 通常我們需要將採用ES6編寫的程式碼轉換成目前已經支援良好的ES5程式碼,包含如下: 將新的ES6語法用ES5實現,例如ES6的class語法用ES5的prototype實現; 為新的API注入polyfill,例
【python3的進階之路二】因特網客戶端編程
網絡流 message world! 3.6 login 三元組 移除 元組 類對象 一、文件傳輸 1.1 文件傳輸因特網協議 最流行的協議包括文件傳輸協議(FTP)、UNIX到UNIX復制協議(UUCP)、用於Web的超文本傳輸協議(HTTP)。另外,還有(U
程式設計師進階之路(C、C++、Java、Python經典書籍及學習順序)
程式設計師進階之路 初級: 《計算機程式的構造和解釋》 C語言: 1.《C語言程式設計:現代方法:第2版》 2.《C Primer Plus 第五版》 3.《C程式設計語言(第2版·新版)》 4.《C和指標》 5.《C專家程式設計》 6.《C 陷阱與缺陷》 7.《資料結構C
Android進階之自定義View(1)實現可換行的TextView
今天來一起學習一下最簡單的自定義view,自己動手寫一個MyTextView,當然不會像系統的TextView那麼複雜,只是實現一下TextView的簡單功能,包括分行顯示及自定義屬性的處理,主要目的是介紹自定義view的實現的基本思路和需要掌握的一些基礎知
JavaScript的進階之路(二)函數簡介,變量、作用域和內存問題
ret 優化 person get 簡介 web瀏覽器 都是 add 是把 <h3>ECMAScript中函數不存在函數簽名的概念,沒有重載</h3><h3>無需指定返回值,可以在任何時候返回任何值。未指定返回值的函數,返回的是一個特殊
Pandas使用DataFrame進行資料分析比賽進階之路(二):日期資料處理:按日期篩選、顯示及統計資料
首先,表格的資料格式如下: 1、獲取某年某月資料 data_train = pd.read_csv('data/train.csv') # 將資料型別轉換為日期型別 data_train[
【SSH進階之路】Struts基本原理 + 實現簡單登錄(二)
target doctype 掌握 pack insert enter snippet file manage 上面博文,主要簡單的介紹了一下SSH的基本概念,比較宏觀。作為剛開始學習的人可以有一個總體上的認識,個人覺得對學習有非常好的輔助功能,它不不過
毛毛Python進階之路6——MySQL 資料庫(二)
毛毛Python進階之路6——MySQL 資料庫(二) 一、對於自增 show create table 表名; # 查看錶是怎樣建立的。 show create table 表名\G; #將某個表旋轉90度 alter table 表名 AUTO_INCREMENT=
Python基礎進階之路(四)之元組、字典和字符串
python基礎 tag 基礎 block 場景 分隔 應用場景 agg bsp 元組 元組的定義 Tuple(元組)與列表類似,不同之處在於元組的 元素不能修改 元組 表示多個元素組成的序列 元組 在 Python 開發中,有特定的應用場景 用
Python基礎進階之路(四)之元組、字典和字串
元組 元組的定義 Tuple(元組)與列表類似,不同之處在於元組的 元素不能修改 元組 表示多個元素組成的序列 元組 在 Python 開發中,有特定的應用場景 用於儲存 一串 資訊,資料 之間使用
Java高階架構師系統進階之路全套視訊免費獲取(Dubbo、Redis、Netty、zookeeper Spring cloud、分散式、高併發等架構技術)
效能調優 03 Spring原始碼分析 04 Spring MVC原始碼分析 05 Mybatis原始碼解析 06 網際網路分散式架構思維 07 架構開發基礎之
(二)Django進階之路 自定義管理器
在 Test 模型中構造管理器子類, 並同步如下資料庫 from django.db import models class Test(model.Model): test_id =
Android進階之路(二) -- NDK初探
繼續學習NDK開發,今天來實現一個簡單的計算器功能,NativeUtil類中有一個靜態的native方法,它接收三個引數,分別是兩個運算元和一個操作符,並且返回C的計算結果。NativeUtil類定義如下public class NativeUtil { static
【SSH進階之路】Struts基本原理 + 實現簡單登入(二)
上面博文,主要簡單的介紹了一下SSH的基本概念,比較巨集觀,作為初學者能夠有一個整體上的認識,個人認為對學習有很好的輔助功能,它不僅僅是一個“瞭望塔”,更是檢驗是否真正掌握所有內容的一個前
我的安卓進階之路(二)
這周結束了“活動”,開始了UI的征程。 1、七大控制元件 踩的兩個小坑 第一個,ImageView即圖片控制元件,名稱必須以字母開頭。如1img不行,而img2可以,倒是和變數名的命名相類似。 第
Android進階之路——NDK(二)
上一篇部落格介紹了NDK簡介和環境的搭建以及一個簡單的Demo,這篇準備總結一下JNI呼叫Java物件以及在JNI中開啟執行緒。 ps:這裡說明一下,我是用Android Studio開發的,如果是用Eclipse開發的朋友,是不能直接匯入我的程式,而
React 進階之路(二)
在之前的文章中我們介紹了 React 開發的環境搭建及目錄介紹和整理,本篇文章將介紹 React 建立元件、JSX 語法、繫結資料和繫結物件。 之前我們已經將專案運行了起來,我們再來看一下目錄結構: 其中 App.js 檔案為我們的根元件,裡面的程式碼: 其中 import 為我們需要引入的檔案
koa2 從入門到進階之路 (二)
之前的文章我們已經能夠在本地啟動一個簡單的專案,本章我們來看一下 koa 路由,get 傳值,動態路由。 一、Koa 路由 路由(Routing)是由一個 URI(或者叫路
Python 從入門到進階之路(二)
之前的文章我們對 Python 語法有了一個簡單的認識,接下來我們對 Python 中的 if while for 做一下介紹。 上圖為 if 判斷語句的流程,無論任何語言,都會涉及到判斷問題,if 條件會進行 true 和 false 的判斷,如下: 1 num = 10 2 if num =