從零學腳手架(三)—webpack屬性詳解
如果此篇對您有所幫助,在此求一個star。項目地址: OrcasTeam/my-cli
在上一篇中,介紹了webpack的entry、output、plugins屬性。
在這一篇,接著介紹其它配置屬性。
mode
這個屬性在上一篇中使用過一次,設置webpack編譯模式的,那麼這個屬性到底是什麼東西呢?
打包器是將開發環境程式碼編譯為可部署環境程式碼
搭建的工程化程式碼基本都無法直接運行在瀏覽器,所以本地測試也都是使用打包編譯後的程式碼預覽。
但是本地開發預覽又必須具有程式碼可讀性、可調試性等條件。
webpack為了解決這個問題,就提供了兩種打包模式:開發模式(development)和發布模式(production)
由mode屬性設置
{
mode:'development'
}
也可以使用CLI參數進行設置
🐋🐋
- webpack默認使用的是發布模式(production),缺少mode屬性執行時,webpack會進行提示
- CLI參數設置 優先順序要高於 webpack.config.js 文件設置。
- 更好的方案是使用兩個webpack.config.js文件,腳手架一般都是這樣區分,在之後介紹webpack merge時處理
測試兩種模式的區別時,最直觀區別就是查看編譯生成的程式碼是否進行了壓縮:在production模式下,webpack會預設壓縮plugin
webpack中的mode屬性值其實具有三個:development、production、none
none屬性值與兩者的區別只是沒有預設任何插件
🐋🐋🐋 development和production兩種模式只是針對不同場景下功能差異化的區分,其實現具體的功能還是使用的插件
為了配置簡單化,development和production兩種模式都預設了一些基本插件。
下面來介紹下 development和production兩種模式中的預設的部分功能
development
process.env.NODE_ENV
development模式時,webpack使用內置DefinePlugin
預設了一個環境變數屬性process.env.NODE_ENV,屬性值為development
開發人員可以編寫業務程式碼時根據process.env.NODE_ENV屬性判斷當前編譯模式,以此執行不同環境中的程式碼。
process.env.NODE_ENV屬性和DefinePlugin稍後詳細介紹
設置模組和模組名稱設置有效性
development模式時,webpack會將JS模組、模組名稱設置為有效名稱,用來方便調試
[email protected]版本設置程式碼可讀性使用的是webpack內置的plugin:NamedModulesPlugin和NamedChunksPlugin
[email protected]版本設置程式碼可讀性使用的是 optimization.moduleIds 和optimization.chunkIds 兩個屬性。
但根源也是使用內置plugin:NamedModuleIdsPlugin和NamedChunkIdsPlugin
optimization: {
moduleIds: 'named',
chunkIds: 'named',
}
optimization屬性是webpack提供的優化屬性,與mode一樣,只是為了方便管理,其根源還是使用插件設置的。
設置devtool屬性
development模式時,webpack會將devtool屬性設置為eval
devtool屬性是控制SourceMap文件如何生成的。SourceMap是用於將原始模組文件與打包後的程式碼映射文件。用於調試使用。具體稍候介紹
production
process.env.NODE_ENV
production模式時,webpack使用內置DefinePlugin
預設一個環境變數屬性process.env.NODE_ENV,屬性值為production
開發人員可以編寫業務程式碼時根據process.env.NODE_ENV屬性判斷當前編譯模式,以此執行不同環境中的程式碼。
process.env.NODE_ENV屬性和DefinePlugin稍後詳細介紹
設置模組和模組名稱混淆
production模式時,webpack將 JS模組、模組名稱進行混淆,以保證程式碼安全性
[email protected]版本設置程式碼可讀性使用的是webpack內置的plugin:NamedModulesPlugin和NamedChunksPlugin
[email protected]版本設置程式碼可讀性使用的是 optimization.moduleIds 和optimization.chunkIds 兩個屬性。
但根源也是使用內置plugin:DeterministicModuleIdsPlugin和DeterministicChunkIdsPlugin
optimization: {
moduleIds: 'deterministic',
chunkIds: 'deterministic',
}
程式碼壓縮
production模式時,webpack 開啟了程式碼壓縮優化 ,使用terser-webpack-plugin庫對打包生成程式碼進行壓縮
🐋🐋 [email protected]默認使用terser-webpack-plugin壓縮程式碼,[email protected]版本及之前版本,默認使用的壓縮庫為uglifyjs-webpack-plugin。但uglifyjs-webpack-plugin已停止維護
作用域提升
production模式時,webpack 會使用內置的ModuleConcatenationPlugin 對程式碼的作用域進行提示。用於減少打包生成的程式碼量和執行速度。
錯誤處理
production模式時,webpack 會預設內置NoEmitOnErrorsPlugin 。
打包編譯時,如果出現程式碼錯誤,則不在生成程式碼。用於避免程式碼錯誤程式碼依然打包成功
🐋🐋[email protected]和[email protected]對於development和production 預設功能具有一定的差異,具體請參考 webpack5-mode、webpack4-mode、顯微鏡下的webpack4的新特性:mode詳解
DefinePlugin
在development和production兩種模式中, 都設置了一個環境變數屬性:process.env.NODE_ENV,只是屬性值不相同。
環境變數用於編寫業務程式碼時 針對不同環境下的差異化程式碼。例如調用第三方SDK時:區分開發環境 和正式環境。
當然可以選擇每次發版時手動修改配置,只要自己不會覺得麻煩。
做一個測試
在/src/index.js中輸出process.env.NODE_ENV屬性
在執行yarn start
後查看打包生成程式碼會看到process.env.NODE_ENV 替換為了development字元串
同樣如果執行yarn build
process.env.NODE_ENV 屬性 會替換成production字元串
這就是 process.env.NODE_ENV環境變數的作用,webpack 在打包編譯時會將設置的環境變數屬性值進行替換,可以在編寫業務程式碼時進行環境判斷。
webpack 使用了內置的DefinePlugin設置 process.env.NODE_ENV。
當然也可以使用DefinePlugin設置自定義環境變數。具體詳情請參考:官網
const webpack = require("webpack");
{
plugins:[
new webpack.DefinePlugin({ "global_a": JSON.stringify("我是一個打包配置的全局變數") }),
]
}
devtool
在development模式中會設置devtool屬性。
devtool屬性也是webpack提供的一個屬性項。用於設置javascript-source-map
我們都看過打包編譯生成的程式碼,哪怕是development模式下生成的,也是超級混亂。
而想要對這些程式碼調試排查錯誤,那簡直是個噩夢。
對於這個問題,Google提供了一種工具叫做:javascript-source-map
javascript-source-map提供一個映射資訊,將 打包編譯生成的程式碼 與 開發編寫的程式碼文件 進行映射,調試時直接針對 開發編寫的程式碼文件進行調試。
🐋 source-map詳細介紹請參考阮一峰老師的:JavaScript Source Map 詳解
webpack提供了devtool屬性來設置javascript-source-map
development模式 devtool屬性默認值為 eval;
production模式 devtool屬性默認值為 false(none)
eval屬性值生成的程式碼都是由eval語法編譯,並提供了一個sourceURL屬性用於指向文件源路徑
devtool屬性具有非常多的屬性值,不同的屬性值 操作具有差異 和 打包消耗時間不同。
有的屬性值會生成一個.map文件,個文件中存放映射資訊,有的直接在生成文件中顯示映射資訊。
在此就不不詳細介紹devtool,有興趣的朋友可以參考官網自行測試
{
// 屬性可以設置為false和字元串
devtool:false; // 'eval'
}
optimization
webpack針對程式碼優化管理,提供了optimization屬性進行管理。
就像剛才介紹的optimization.moduleIds和optimization.chunkIds提供了對模組和模組名稱管理。
但其根源還是使用了插件進行管理,屬性只是為了方便管理。
optimization對象具有好多屬性,在此也不詳細介紹,
只介紹optimization.minimize和optimization.minimizer。這兩個也是經常被使用到屬性。
minimize和minimizer
minimize
先來做一個測試,將optimization.minimize手動改為false
optimization:{
minimize:false
}
此時使用yarn build
執行打包可以看到程式碼並沒有進行壓縮
也就是optimization.minimize屬性是控制程式碼壓縮的。
而production模式只是將optimization.minimize設置為了true
optimization:{
// 開啟默認優化
minimize:true
}
minimizer
在optimization對象中還具有一個minimizer屬性,這個屬性和plugins屬性功能相同,都是用來設置plugin的。
而兩者的區別在於:optimization.minimizer會受到optimization.minimize屬性的管理
optimization.minimizer屬性會受到optimization.minimize屬性的控制:
如果optimization.minimize屬性值為false,那麼就不載入設置在optimization.minimizer屬性中的plugin
也就是optimization.minimize是控制optimization.minimizer屬性的開關。
terser-webpack-plugin 默認情況下是設置在optimization.minimizer屬性中,所以optimization.minimize屬性設置為false 程式碼會不壓縮。
如果terser-webpack-plugin 手動設置在plugins屬性中,
那麼就算 optimization.minimize 為 false,程式碼依然會壓縮。
🐋 webpack已經依賴了terser-webpack-plugin,所以就不需要再安裝
🐋 [email protected]以下默認使用的壓縮褲為uglifyjs-webpack-plugin,好多文章都是以uglifyjs-webpack-plugin為基礎講解的,不過uglifyjs-webpack-plugin目前不再維護,[email protected]開始改為了terser-webpack-plugin
optimization.minimize和optimization.minimizer是webpack為方便管理提供的屬性。
在配置時可以將關於優化的plugin設置在optimization.minimizer屬性,由optimization.minimize統一管理。
而webpack提供的一系列默認值提供了最小配置。
代價卻提高了webpack學習成本。讓很多人對這些屬性感到迷惑。
terser-webpack-plugin
terser-webpack-plugin 作為[email protected]默認的壓縮工具。在此就直接介紹此庫的屬性
🐋🐋 terser-webpack-plugin壓縮對devtool屬性具有一定的要求,只支援none、source-map、inline-source-map、hidden-source-map、nosources-source-map。 像eval生成的是字元串。terser-webpack-plugin就沒辦法進行處理
在剛才手動設置terser-webpack-plugin時沒有添加任何參數。
而terser-webpack-plugin是有很多配置項的,配置項通過構造函數傳遞。
terser-webpack-plugin第一層參數主要對於文件多執行緒的設置。
const TerserPlugin = require('terser-webpack-plugin');
{
optimization: {
// 配置可優化
minimize: true,
minimizer: [
new TerserPlugin({
// 指定壓縮的文件
include: /\.js(\?.*)?$/i,
// 排除壓縮的文件
// exclude:/\.js(\?.*)?$/i,
// 是否啟用多執行緒運行,默認為true,開啟,默認並發數量為os.cpus()-1
// 可以設置為false(不使用多執行緒)或者數值(並發數量)
parallel: true,
// 可以設置一個function,使用其它壓縮插件覆蓋默認的壓縮插件,默認為undefined,d,
minify: undefined,
// 是否將程式碼注釋提取到一個單獨的文件。
// 屬性值:Boolean | String | RegExp | Function<(node, comment) -> Boolean|Object> | Object
// 默認為true, 只提取/^\**!|@preserve|@license|@cc_on/i注釋
// 感覺沒什麼特殊情況直接設置為false即可
extractComments: false,
// 壓縮時的選項設置
terserOptions: {}
})
]
}
}
-
include:指定壓縮的文件
屬性可設置為:String、String[]、Regex
默認值為:undefined
-
exclude:排除壓縮的文件
屬性可設置為:String、String[]、Regex
默認值為:undefined
-
parallel:是否啟用多執行緒運行
屬性可設置為:Boolean、Number
屬性值為false:不啟動多執行緒
屬性值為true:啟動多執行緒,多執行緒數量為:os.cpus()-1
屬性值為Number:表示使用的多執行緒數量
默認值為:true
-
minify:設置其它壓縮工具覆蓋terser-webpack-plugin
此屬性可以設置一個函數,函數內允許使用其它壓縮工具替代terser-webpack-plugin, 其實相當於做了一個攔截,基本上不會使用此屬性。 詳細介紹可以參考 官方
屬性可設置為:Function
默認值為undefined
-
extractComments:是否將程式碼注釋提取到一個單獨的文件。
經過壓縮的程式碼都會去除注釋,此屬性就是設置是否提取注釋,個人感覺這個屬性也沒什麼用。詳細介紹可以參考 官方
屬性可設置為:Boolean、String、RegExp、Function<(node, comment) -> Boolean | Object>、 Object
屬性值為false或者函數返回false:表示不提取
屬性值為String時: all表示全部提取。some表示使用默認正則匹配:/^**!|@preserve|@license|@cc_on/i
屬性值為true或者函數返回true時:表示提取,使用默認正則匹配:/^**!|@preserve|@license|@cc_on/i
屬性值為Regex時:自定義提取規則。
屬性值為Object時:允許自定義提取條件。
默認值為true
-
terserOptions:設置壓縮選項
此屬性才是詳細設置壓縮選項的參數。
屬性可設置為:Object
terserOptions屬性
先來做一個測試,在index.js中創建這麼一個函數
使用默認壓縮配置進行打包編譯,結果可以看到生成的程式碼只有真實執行的程式碼。
默認terser-webpack-plugin配置基本上做到了最優解。
🐋 如果將var a = 1改為let a = 1,則結果有些不一致,這是由於ES6問題,有興趣的諸君可以測試一下
terser-webpack-plugin配置屬性中:terserOptions.compress屬性才是控制壓縮。
terserOptions.compress 設置類型為 Boolean、Object。
接下來將此屬性設置為false。查看打包編譯程式碼,可以發現,程式碼並沒有被壓縮,只是改變了屬性名稱和函數函數
{
optimization: {
// 配置可優化
minimize: true,
minimizer: [
new TerserPlugin({
// 壓縮時的選項設置
terserOptions: {
compress:false
}
})
]
}
}
terserOptions這一層中的設置主要是對程式碼中屬性名稱、函數名稱。等一系列的設置
{
optimization: {
// 配置可優化
minimize: true,
minimizer: [
new TerserPlugin({
// 壓縮時的選項設置
terserOptions: {
// 是否保留原始函數名稱,true代表保留,false即保留
// 此屬性對使用Function.prototype.name
// 默認為false
keep_fnames:false,
// 是否保留原始類名稱
keep_classnames:false,
// format和output是同一個屬性值,,名稱不一致,output不建議使用了,被放棄
// 指定壓縮格式。例如是否保留*注釋*,是否始終為*if*、*for*等設置大括弧。
format: {comments:true},
output: undefined,
// 是否支援IE8,默認不支援
ie8:true,
// ·壓縮配置
compress: { },
}
})
]
}
}
-
keep_fnames:是否保留原始函數名稱
剛才測試看到了,默認情況下會更改函數名稱,此屬性就是設置是否保留函數名稱。
屬性可設置為:Boolean
屬性值為false:表示不保留原始名稱
屬性值為true:表示保留原始名稱
默認值為false
-
keep_classnames: 是否保留原始類名稱
與keep_fnames屬性類似,只不過設置的是類名稱
屬性可設置為:Boolean
屬性值為false:表示不保留原始名稱
屬性值為true:表示保留原始名稱
默認值為false
-
format/output:指定壓縮格式。例如是否保留注釋,是否始終為if、for等設置大括弧。
format和output的配置相同。output官方不再推薦使用。這個屬性就不介紹,具體請參考官方
屬性可設置為:Object
默認值為null
-
ie8:是否支援IE8
屬性可設置為:Boolean
默認值為false
-
compress:設置壓縮選項
屬性可設置為:Boolean、Object
屬性值為false:表示不壓縮。
屬性值為object:自定義壓縮設置。
下面介紹下terserOptions.compress的配置。terserOptions.compress只介紹部分屬性 。其它設置,有興趣的朋友可以查看官方
{
optimization: {
// 配置可優化
minimize: true,
minimizer: [
new TerserPlugin({
// 壓縮時的選項設置
terserOptions: {
compress: {
// 是否使用默認配置項,這個屬性當只啟用指定某些選項時可以設置為false
defaults: false,
// 是否移除無法訪問的程式碼
dead_code: false,
// 是否優化只使用一次的變數
collapse_vars: true,
warnings: true,
// 是否刪除所有 console.*語句,默認為false,這個可以在線上設置為true
drop_console: false,
// 是否刪除所有debugger語句,默認為true
drop_debugger: true,
// 移除指定func,這個屬性假定函數沒有任何副作用,可以使用此屬性移除所有指定func
// pure_funcs: ['console.log'], //移除console
}
}
})
]
}
}
-
defaluts:是否使用默認配置項
此屬性表示是否使用官方設置默認配置項
屬性可設置為:Boolean
默認值為true
-
dead_code:是否移除無法訪問的程式碼
屬性可設置為:Boolean
默認值為true
-
collapse_vars:是否優化只使用一次的變數
此屬性表示是否將只使用一次的變數直接進行替換優化
屬性可設置為:Boolean
默認值為true
-
drop_console:是否刪除所有console語句
此屬性可以在發布時設置為true
屬性可設置為:Boolean
默認值為false
-
drop_debugger:是否刪除所有debugger語句
屬性可設置為:Boolean
默認值為true
-
pure_funcs:移除指定的函數。
此屬性可以設置移除指定的函數,但是需要缺點要移除的函數沒有任何副作用(沒有使用),有興趣的朋友可以測試刪除自定義函數
terser-webpack-plugin配置項還有好多,但是一般使用默認屬性即可,
會被使用到可能也就是 :
terserOptions.compress.drop_console去除所有console和parallel來設置多執行緒
其它一般都是默認值即可。
🐋🐋 暫時先將optimization.minimize屬性設置false,可以更方便的查看生成程式碼
loader
在上一篇文章說過:webpack是一個JavaScript應用程式的靜態模組打包器,其本身並不支援非JS模組。
但webpack提供了將 非JS模組 轉換為JS模組的功能—loader
loader相當於一個攔截器,將指定文件進行編譯為JS模組,再傳遞給webpack。
在這裡先不學習具體的loader ,只介紹下loader 的配置語法。
loader的配置是在module.rules屬性,module.rules是一個Array類型屬性。
數組每一項都可以設置 攔截文件使用指定loader 。
{
module:{
rules:[
{
// test:/\.css$/,
// include:path.join(__dirname,'src'),
// exclude:path.join(__dirname,'node_modules'),
// // 字元串形式
// use:'css-loader',
// 數組形式,可以設置多個loader
// use:[
// {
// loader:'css-loader',
// options:{
//
// }
// }
// ]
}
]
}
}
-
test:設置攔截文件
使用此屬性設置攔截的文件。例如:/.css$ 表示攔截所有的.css文件。使用Regex可以攔截多種文件類型使用同一loader
屬性可設置為:Regex
-
include:包含攔截的文件目錄。
此屬性可以設置攔截指定目錄的文件,一般使用此屬性設置只攔截/src目錄中文件
屬性可設置為:String
-
exclude:排除攔截的文件目錄。
此屬性與include類似,只不過功能相反,指定要排除的目錄。一般使用此屬性排除node_modules目錄。
此屬性與include只使用一種。
屬性可設置為:String
-
use:攔截到的文件所使用的loader。
屬性可設置為:String、Array
屬性值為String:設置loader 名稱
屬性值為Array:可以指定多個loader 處理,並且可以對每一個loader 設置屬性配置。
🐋🐋🐋 當指定多個loader 時,loader 載入順序為從右往左。具體請參考Webpack的Loader為什麼是從右往左寫
resolve
resolve 是webpack提供的一個屬性,用來配置打包編譯時的模組解析規則。
resolve是一個Object類型,具有不少的屬性配置。
在此只介紹三個常用的屬性。其它屬性,有興趣的朋友可以去參考中文官網
alias
使用vue-cli這類腳手架,開發時引入本地文件模組,通常可以使用一個符號(@)來代替/src工作目錄。
這個功能就是resolve.alias提供的。
resolve.alias屬性可以對一個指定路徑設置別名。打包編譯時會將設置別名替換為配置的真實路徑
{
resolve: {
alias:{
// 設置路徑別名
'@':path.join(__dirname,'src'),
'~': path.resolve(__dirname, '../src/assets')
},
}
}
此時在引用文件模組時,就可以使用@來代替/src工作目錄(工作根目錄)
🐋 webpack允許設置除關鍵字外的任意符號作為別名。vue-cli這類腳手架一般都預設符號(@)代替/src工作目錄。
extensions
使用vue-cli這類腳手架,開發時引入本地文件模組。很常見的一種行為就是不需要添加後綴名稱。
這個功能就是resolve.extensions提供的。
resolve.extensions功能允許設置多個後綴名稱。導入文件模組時可以忽略文件後綴名稱,
打包編譯時按照配置綴順序依次匹配,直到尋到第一個匹配文件或報錯(找不到匹配文件)。
resolve.extensions屬性類型為Array,默認值為:[‘.js’, ‘.json’],也就是可以忽略JS文件和JSON文件後綴
下面將resolve.extensions設置為[‘.json’]做一個測試
{
resolve: {
extensions:['.json']
}
}
此時由於引用index2.js時還是沒有添加後綴,打包編譯時就直接報錯了。
而引用index2.js添加.js後綴名稱才可以打包成功。。
vue-cli、react-cli這類腳手架都會在resolve.extensions屬性配置自己文件類型的後綴名稱。
在此以react-cli為例
🐋🐋🐋 打包編譯時匹配resolve.extensions,使用的是隊列形式。Array從先到後
mainFiles
使用vue-cli這類腳手架,開發時引入本地文件模組,有一種常見方式只指定其文件所在目錄,並沒有指定文件名稱。
這種方式常見於以 目錄為組件單元的程式碼風格。例如antd,就是以目錄為組件單元。
這個功能是由resolve.mainFiles屬性提供的。
resolve.mainFiles允許設置多個文件名稱,導入文件模組時可以忽略此文件名稱。
打包編譯時按照配置綴順序依次匹配,直到尋到第一個匹配文件或報錯(找不到匹配文件)。
resolve.extensions屬性類型為Array,默認值為:[‘index’],也就是可以忽略index名稱的文件。
🐋🐋 使用resolve.mainFiles時需要設置resolve.extensions,目錄是沒有後綴的,需要設置忽略後綴,否則會報錯:
下面將resolve.mainFiles設置為[‘index’,’main’]做測試
{
resolve: {
extensions:['.js','.json'],
mainFiles:['index','main'],
}
}
可以看到, 可以看到導入/demo/main.js時,忽略了文件名稱,但是依然打包編譯、導入成功
🐋🐋🐋 打包編譯時匹配resolve.mainFiles,使用的也是隊列形式。Array從先到後
🐋🐋🐋 個人建議使用目錄結構方式組織組件
context
配置webpack時,使用文件目錄時都使用了絕對地址:path.join(__dirname, …)
webpack其實提供了一個context屬性:在配置項中允許 以此目錄為基準 設置 相對目錄。
不過context屬性個人感覺並不太好用。
context屬性String類型,設置一個絕對路徑的目錄地址
context默認值為當前項目根目錄,也就是package.json文件所在目錄
context屬性默認值為當前項目根目錄,所以可以直接使用相對路徑
{
entry: './src/index.js' ,
}
此時執行yarn build
打包也可以進行打包成功。
也可以使用context指定其它目錄為基準目錄
{
context: path.join(__dirname, './src'),
// 入口文件
// 字元串形式
entry: './index.js' ,
}
但context屬性具有一定的缺陷
output不允許相對路徑
並不是所有的屬性都可以設置為相對路徑
例如output屬性就只允許使用絕對路徑。
配置屬性中既有相對路徑又有絕對路徑,對於我這個強迫症來說感覺怪怪的。
🐋 output屬性只允許使用絕對路徑應該是為了保證輸出地址的準確安全性。
基準絕對路徑
個人比較喜歡的方案就是自定義一個root目錄。
在配置文件中 root目錄 去設置路徑
const config = {
root: path.join(__dirname, './'),
};
const {
entry: path.join(config.root, 'src/index.js'),
output: {
path: path.join(config.root, 'dist'),
filename: '[name]_[contenthash].js'
},
}
至於自定義root屬性而不直接使用__dirname原因是:
個人感覺自定義屬性方便控制。 例如更換配置文件目錄,直接使用__dirname,所有目錄地址都需要更改,而自定義絕對路徑基準就只需要更改root目錄即可
當然真實開發不會出現此類情況。
使用context屬性還是絕對路徑都無傷大雅,個人習慣罷了,只要文件路徑正確即可
總結
🐋🐋🐋
- webpack提供了兩種打包模式:開發測試打包編譯(development)和線上發布打包編譯(production) 。兩種打包模式能夠更加方便管理插件
- webpack對development和 production 都預設了一些基礎功能,大大減少了開發時的配置
- source-map是Google提供的打包編譯後程式碼與開發程式碼的一種映射文件,主要用途是方便開發人員調試
- optimization屬性是webpack提供的控制優化的屬性, optimization只是一系列優化功能的集合,主要是為了方便管理,本質還是由插件完成功能
- resolve屬性提供了打包編譯時的解析規則,
本文參考
- webpack官網
- webpack5-mode
- webpack4-mode
- webpack4的新特性:mode詳解
- uglifyjs-webpack-plugin
- terser-webpack-plugin
- terser Github
- terser-webpack-plugin Github
- Webpack的Loader為什麼是從右往左寫?
webpack.config.js
const path = require('path')
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const config = {
root: path.join(__dirname, './'),
}
const modules = {
// 入口文件
// 字元串形式
entry: path.join(config.root, 'src/index.js'),
// 對象形式
// entry:{
// 'index': path.join(config.root, 'src/index.js'),
// },
// 輸出文件
// 字元串形式
// output:path.join(config.root, './dist/[name].js')
//對象形式
output: {
// 輸出文件的目錄地址
path: path.join(config.root, 'dist'),
// 輸出文件名稱,contenthash代表一種快取,只有文件更改才會更新hash值,重新打包
filename: '[name]_[contenthash].js'
},
//devtool:false, //'eval'
// module:{
// rules:[
// {
// test:/\.css$/,
// include: ath.join(config.root,'src'),
// exclude: path.join(config.root,'node_modules'),
// //// 字元串形式
// // use:'css-loader',
// // 數組形式,可以設置多個loader
// // use:[
// // {
// // loader:'css-loader',
// // options:{
// //
// // }
// // }
// // ]
// }
// ]
// }
optimization: {
// 暫時關閉壓縮優化,方便觀察打包生成程式碼
minimize: false,
minimizer: [
new TerserPlugin({
// 指定壓縮的文件
include: /\.js(\?.*)?$/i,
// 排除壓縮的文件
// exclude:/\.js(\?.*)?$/i,
// 是否啟用多執行緒運行,默認為true,開啟,默認並發數量為os.cpus()-1
// 可以設置為false(不使用多執行緒)或者數值(並發數量)
parallel: true,
// 可以設置一個function,使用其它壓縮插件覆蓋默認的壓縮插件,默認為undefined,
minify: undefined,
// 是否將程式碼注釋提取到一個單獨的文件。
// 屬性值:Boolean | String | RegExp | Function<(node, comment) -> Boolean|Object> | Object
// 默認為true, 只提取/^\**!|@preserve|@license|@cc_on/i注釋
// 感覺沒什麼特殊情況直接設置為false即可
extractComments: false,
// 壓縮時的選項設置
terserOptions: {
// 是否保留原始函數名稱,true代表保留,false即保留
// 此屬性對使用Function.prototype.name
// 默認為false
keep_fnames:false,
// 是否保留原始類名稱
keep_classnames:false,
// format和output是同一個屬性值,,名稱不一致,output不建議使用了,被放棄
// 指定壓縮格式。例如是否保留*注釋*,是否始終為*if*、*for*等設置大括弧。
format: {
comments:false,
},
output: undefined,
// 是否支援IE8,默認不支援
ie8:false,
compress: {
// 是否使用默認配置項,這個屬性當只啟用指定某些選項時可以設置為false
defaults:false,
// 是否移除無法訪問的程式碼
dead_code:false,
// 是否優化只使用一次的變數
collapse_vars:true,
warnings:true,
// 是否刪除所有 console.*語句,默認為false,這個可以在線上設置為true
drop_console: false,
// 是否刪除所有debugger語句,默認為true
drop_debugger:true,
// 移除指定func,這個屬性假定函數沒有任何副作用,可以使用此屬性移除所有指定func
// pure_funcs: ['console.log'], //移除console
},
}
})
]
},
plugins: [
new HtmlWebpackPlugin({
// HTML的標題,
// template的title優先順序大於當前數據
title: 'my-cli',
// 輸出的html文件名稱
filename: 'index.html',
// 本地HTML模板文件地址
template: path.join(config.root, 'src/index.html'),
// 引用JS文件的目錄路徑
publicPath: './',
// 引用JS文件的位置
// true或者body將打包後的js腳本放入body元素下,head則將腳本放到中
// 默認為true
inject: 'body',
// 載入js方式,值為defer/blocking
// 默認為blocking, 如果設置了defer,則在js引用標籤上加上此屬性,進行非同步載入
scriptLoading: 'blocking',
// 是否進行快取,默認為true,在開發環境可以設置成false
cache: false,
// 添加mate屬性
meta: {}
}),
new CleanWebpackPlugin({
// 是否假裝刪除文件
// 如果為false則代表真實刪除,如果為true,則代表不刪除
dry: false,
// 是否將刪除日誌列印到控制台 默認為false
verbose: true,
// 允許保留本次打包的文件
// true為允許,false為不允許,保留本次打包結果,也就是會刪除本次打包的文件
// 默認為true
protectWebpackAssets: true,
// 每次打包之前刪除匹配的文件
cleanOnceBeforeBuildPatterns: ['**/*'],
// 每次打包之後刪除匹配的文件
cleanAfterEveryBuildPatterns:["*.js"],
}),
new webpack.DefinePlugin({ "global_a": JSON.stringify("我是一個打包配置的全局變數") }),
],
resolve: {
alias:{
// 設置路徑別名
'@': path.join(config.root, 'src') ,
'~': path.join(config.root, 'src/assets') ,
},
// 可忽略的後綴
extensions:['.js', '.json'],
// 默認讀取的文件名
mainFiles:['index', 'main'],
}
}
// 使用node.js的導出,將配置進行導出
module.exports = modules