webpack是什么
打包器
在使用 webpack 之前,首先需要明白 webpack 到底是個什么東西。
幾乎所有文章(包括官網)中都說webpack是一個 打包器 ,用於打包所有資源。
webpack確實是一個 打包器 ,但是對於不知道打包器的朋友來說還是會有些迷惑。
拋開 webpack 去看問題本質。
當前時代,前端的作用越來越大,對於寫過MVC或者更早的JSP或ASP.NET的朋友可能更有體會,那時代的前端只是作為展示作用。
隨着 移動端和 Node.JS 的崛起,前端進入了新的時代。
前端與后端進行了分離,前端開始獨立部署,逐漸走入了工程化的步伐。
首先對於獨立部署,就會有很多的工作需要完成,最容易想到的就是壓縮。
MVC時代,壓縮工作都是由后端進行完成。但是對於已經獨立部署的前端,這項工作只能由前端自行完成。
當然還有逐漸更新的JS、CSS和瀏覽器兼容性等一系列問題。
這些問題其實可以總結為 可部署環境代碼 和 開發環境代碼 之間的沖突。
那么能不能提供一個橋梁來連接兩種環境代碼呢?最好能夠提供一個 黑匣子 ,能夠讓我們使用一個命令將 開發環境代碼 編譯為 可部署環境代碼
打包器 就是這么一個 黑匣子

打包器是個 黑匣子 這是對於大部分寫業務的程序員來說的,他們只需要完成業務模塊。
但是對於項目管理者,打包器 就必須掌握,畢竟由於前端的特殊性,導致需要自己配置屬於自己項目的 打包器 。所以對於 打包器 的了解也基本屬於前端必修課。
webpack
webpack 就屬於一個 打包器 工具。目前市面上也有好多其它 打包器 工具:grunt,gulp,rollup、還有尤大新開發vite。每一個 打包器 都有各自的優缺點。
不過截止到目前最流行的還是webpack 。所以在此也是以webpack作為學習、
webpack是一個優秀的 JavaScript應用程序 的靜態模塊打包器,具有高度可配置的優勢,因此也被業界稱為最麻煩之一。
前面說過:打包器 就是將 開發環境代碼 進行編譯為 可部署環境代碼 。
而不同的項目對 可部署環境代碼 的要求又不一致。所以 webpack 並沒有提供一個全而大功能,而只是提供了一個 核心引擎, 只負責 JS 文件的依賴打包,其它功能使用 插件化 進行配置 。
🐋🐋🐋 這里說的 插件化 並不是指的 webpack 中的 plugin ,而是 擴展 的意思,為了 避免和 webpack 中 plugin 翻譯歧義。 webpack 中 plugin 就不做翻譯。
一般開發人員使用 webpack 實現某個功能時,只需要尋找符合自己需求的插件就可以。插件則由強大的社區維護。
社區中具有海量的 插件 ,相同功能的都有好多。所以,在學習 webpack 時,
我個人建議轉換一下思想: 不要想這個東西是什么,而要想我們需要什么 。根據自己需求去尋找合適的 插件。
經過了這么多年的發展,大部分功能的 插件 已經具有了最優解,形成了 插件 固定化。就像現在大部分語言運行環境的 GC算法 都是 引用跟蹤算法 一樣。
在上一篇文章中講到 package.json 文件中的 devDependencies 留了一個問題:什么是開發環境依賴。
其實就可以總結出:構建工程化依賴環境 時使用的依賴庫。
🐋 構建工程化依賴環境 包括 打包器、還有 eslint 和 單元測試庫
webpack 基本使用
webpack安裝
在之前已經安裝了 webpack@5.24.0 依賴庫,
在這里只需要安裝 webpack-cli 即可,webpack-cli 類似一個簡易的客戶端,用來以 webpack 連接對應服務。
如果不安裝 webpack-cli 執行 webpack
命令時會提示安裝 webpack-cli
yarn add -D webpack-cli@4.5.0 // 安裝到devDependencies依賴。
在 package.json 文件 scripts 屬性中加入 build:webpack
命令

此時執行 yarn build
就會執行 webpack
命令
雖然會因為沒有配置項而失敗,但 webpack 成功運行了。
🐋 webpack 可以直接使用命令行參數打包文件,不過在此就不贅述,有興趣的朋友可以參考官網
webpack.config.js
在根目錄中創建一個 webpack.config.js,此文件是 webpack 配置項文件
webpack.config.js 文件必須拋出一個 模塊 ,這個 模塊 可以是一個 Object ,也可以是一個返回值為 Object 的函數, webpack 屬性就配置在這個Object中
webpack 執行時會讀取 webpack.config.js 文件模塊,根據此 Object 中的配置信息進行打包編譯

根目錄 webpack.config.js 文件名稱是一個 約定文件名稱。
在不指定配置文件情況下,webpack 會讀取根目錄 webpack.config.js 文件。
當然也可以使用參數指定配置文件,也推薦這樣做,參數指令可以更改配置文件的目錄和名稱

工作目錄
接下來創建 /src 目錄、 /src/index.js 文件

在使用vue-cli、react-cli時都會具有一個 /src 目錄,這是一個 約定的工作目錄。
項目中的代碼文件都存放此目錄下。
當然此目錄名稱可以隨意設置,只不過約定為 /src。
/src/index.js 文件,是一個 entry(入口) 文件。
打包器 作為一個將 開發環境代碼 編譯 為 可部署環境代碼 的橋梁,那么就必須 至少擁有一個 entry(開發環境代碼) 和一個 output(可部署環境代碼)
/src/index.js 就作為這么一個 entry(入口)文件
🐋 index.js 文件名稱也是約定名稱。
wbepack簡單配置
entry、output
既然 打包器 必須有 entry(入口) 和 output(輸出) ,那么就由這兩個屬性開始。
webpack 中就是使用 entry 和 output 作為兩者的屬性名稱,這兩個屬性都可以靈活配置。
const path = require('path')
const modules = {
// 入口文件
// 字符串形式
entry:path.join(__dirname, 'src/index.js'),
// 對象形式
// entry:{
// 'index':path.join(__dirname, 'src/index.js')
// },
// 輸出文件
// 字符串形式
// output:path.join(__dirname, 'dist/[name].js')
//對象形式
output:{
// 輸出文件的目錄地址
path:path.join(__dirname, 'dist'),
// 輸出文件名稱,contenthash代表一種緩存,只有文件更改才會更新hash值,重新打包
filename: '[name]_[contenthash].js'
}
}
// 使用node.js的導出,將配置進行導出
module.exports = modules
-
entry :入口文件。
屬性可設置為:String 、 Object 、 Array
屬性值為 String :直接設置一個入口文件地址
屬性值為 Object :對入口文件進行詳細設置和 可以設置多個入口文件
屬性值為 Array :設置多個入口文件
🐋 webpack 允許設置多個 entry(入口) 文件,打包編譯出多個文件。一般用於 多頁面配置
至於多頁面開發配置,就不在此贅述,有興趣的朋友可以去查閱下 參考資料,單頁面程序直接設置 String 即可
-
output:輸出文件。
屬性可設置為:String 、Object
屬性值為 String :直接設置一個輸出文件地址
屬性值為 Object :輸出文件地址和文件名稱詳細設置
🐋 webpack在設置 output.filename 時,允許使用 [name] 保留 entry 屬性設置的文件名稱。
entry 為 String 時, [name] 為文件名稱
entry 為 Object 時, [name] 為對象的 key
🐋 output 屬性中的 [contenthash] ,是 webpack 提供的一種打包緩存的機制。 webpack 會為打包編譯生成一個 hash 值。只有更改源文件后才會重新打包編譯,生成新的 hash 。 緩存機制一般只會在 生產模式(production) 使用。
webpack還提供了兩個屬性 [hash]、[chunkhash] 設置緩存,具體區別請參考:webpack中hash、chunkhash、contenthash區別
🐋 :__dirname 屬性是 Node.JS 基礎庫中屬性,表示當前文件的絕對路徑。等同於path.dirname()。
🐋 webpack.json 文件中使用了 Node.JS 基礎庫(require('path')) 獲取文件絕對地址,更准確的保證文件目錄的完整性。當然也可以只使用相對地址,不過不推薦
此時使用yarn build
命令就可以執行 webpack 。 最終會在根目錄創建 /dist,並在 /dist 目錄下生成一個 .js 文件。
文件中就是 /src/index.js 中的內容

以上就是一個最簡單的 webpack 配置,只是將 entry(入口) 文件打包編譯到 output(輸出)。
不過 webpack 本身是一個強大的 JavaScript應用程序
接下來做小測試,在 /src 目錄中創建 index2.js ,並在 index.js 中導入

此時再進行yarn build
,/dist 目錄下會自動再創建一個 .js 文件,這個 .js 文件就是這次打包的輸出問題。
從文件中具有 index.js 和 index2.js 內容

🐋🐋 打包后文件會進行壓縮,並且代碼會多出許多 webpack 構建的代碼。
代碼壓縮是因為 webpack 默認使用的是 生產模式(production)。
如果想要不壓縮代碼可以在 webpack.json 文件中添加一個 mode:'development' 屬性,至於這兩個屬性意思下一篇再詳細講解。
這就是webpack 的強大功能之一。 webpack 在打包處理文件時,會遞歸的構建一個 依賴圖(dependency graph) ,根據這個 依賴圖 將所有使用到的 JS模塊 進行打包。
🐋如果在 index.js 將引入 index2.js 的代碼注釋或者刪除,那么 index2.js 文件內容便不會被打包。有興趣的朋友可以自行測試。
plugins
在前面說過,webpack 只提供了一個 核心引擎。大部分功能使用 插件化 進行管理
webpack 配置項提供了一個 plugins 屬性,該屬性是一個 Array 類型,用於設置 plugin。
webpack 在執行時會順序執行 plugins 中的 plugin 。
const modules = {
plugins:[
],
}
// 使用node.js的導出,將配置進行導出
module.exports = modules
🐋🐋 webpack 默認只是一個 JavaScript應用程序 打包器,所以不會處理CSS、Image、TypeScript等 非js模塊,
webpack提供了一個 loader 屬性處理 非JS模塊(將非JS模塊轉換為JS模塊), 而 plugins 則為 webpack 打包時提供其他擴展。兩者結合了促成了 webpack的 插件化 ,使 webpack 可以高度擴展。至於兩者的分工和不同,之后會慢慢了解。
🐋 webpack 提供了自定義 plugin 的方法,具體編寫規則,有興趣的朋友可以去參考 官網
html-webpack-plugin
眾所周知,運行在瀏覽器根源是 HTML 文件,HTML 作為 JS 文件的承載容器,哪怕將所有業務邏輯都交給 JS(Document類型) 完成,依然需要一個 HTML 容器承載,那么就需要提供一個提供的 HTML 文件的功能
webpack 中 html-webpack-plugin 就是完成這個功能的
🐋🐋 HTML 容器也是一個 非JS模塊,那么為什么使用 plugin處理 而不使用loader呢? 我個人的理解為 HTML 的提供是作為一個 JS 容器存在,而並非要轉換為 JS模塊 處理
🐋 webpack社區中 以 -plugin 為單詞為后綴的庫,都為 plugin。而以 -loader 單詞為后綴的庫,都為 loader。 這也是一種約定規則。
yarn add -D html-webpack-plugin@5.2.0
安裝之后,需要在webpack文件中進行引用
const HtmlWebpackPlugin = require('html-webpack-plugin')
const modules = {
plugins: [
new HtmlWebpackPlugin()
]
}
// 使用node.js的導出,將配置進行導出
module.exports = modules
此時執行yarn build
,/dist 目錄便會多出一個 HTML 文件,此 HTML 還引用此次打包的 JS 文件。
在瀏覽器中也可以運行此 HTML ,開發者控制台中會打印 JS 文件中的 Console 語句。

真實項目中,HTML 容器也要設置很多東西。例如: Title 、 Meta 或者 icon 等。
並且在vue-cli中,會有一個 HTML 模板文件。在此 HTML 文件添加信息,並且以此文件進行作為容器。
這些信息都是由 html-webpack-plugin 構造參數提供的。
plugins: [
new HtmlWebpackPlugin({
// HTML的標題,
// template的title優先級大於當前數據
title:'my-cli',
// 輸出的html文件名稱
filename:'index.html',
// 本地HTML模板文件地址
template:path.join(__dirname, '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:{}
})
]
-
title:HTML 的標題,
屬性可設置為: String
此屬性在設置 template 屬性后會失效
-
filename:輸出的 HTML 文件名稱,
屬性可設置為:String
默認值為: index.html
-
template:本地 HTML 模板文件地址。
屬性可設置為:String
使用HTML模板文件時,會將 HTML 模板文件內容原封不動的 copy。
例如下面 HTML 模板引用了jquery,打包后的 HTML 依然存在jquery。
-
publicPath: 引用 JS 文件的目錄路徑。
屬性可設置為:String
例如設置路徑為 ./ ,那么在 HTML 文件引用 JS 時就會為
<script src="./main_XXXXXX.js"></script>
。此屬性提供了更靈活的項目管理,可以將 HTML 文件和 JS 文件打包到不同目錄。
-
inject: 引用編譯后 JS 文件的位置。
屬性可設置為:Boolean 、 head 、 body
屬性值為 false :代表不引用編譯后 JS 文件
屬性值為 true 和 body :在 body 元素最后引用編譯后 JS 文件 。推薦
屬性值為 head:在 head 元素中引用編譯后 JS 文件,不推薦
默認值為 true
-
scriptLoading:設置加載 JS 的方法
屬性可設置為:blocking、defer
默認值為 blocking
具體 blocking 、defer 差別可參考defer和async的區別
-
cache:是否緩存 HTML 文件。
屬性可設置為:Boolean
默認值為 true
開發環境(development)時可以設置為 false
-
meta:設置 meta 屬性
在此列舉了html-webpack-plugin主要部分屬性, 更多屬性可以在npm中查看
template 屬性需要是一個本地的html路徑。

clean-webpack-plugin
之前每次打包編譯都會在 /dist 目錄創建 .js 文件,久而久之, /dist 就會具有好多無用的文件

/dist 目錄往往希望它是一個干凈的目錄,目錄內只具有最新一次的打包生成的文件。這樣就可以直接以此目錄進行發布。
針對這個需求,社區有大佬開發了clean-webpack-plugin。
clean-webpack-plugin 會在每次打包編譯時,清空輸出目錄。
這個求 --- clean-webpack-plugin
yarn add -D clean-webpack-plugin@3.0.0
將此庫直接添加到 plugins 屬性即可
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new CleanWebpackPlugin()
]
此時執行yarn build
/dist 目錄就只剩下本地打包結果。

🐋🐋
clean-webpack-plugin庫使用時為:const { CleanWebpackPlugin } =
與html-webpack-plugin庫: const HtmlWebpackPlugin = 不同。
這是因為html-webpack-plugin使用了默認導出: export default ,
而clean-webpack-plugin導出的是指定類型: CleanWebpackPlugin
默認導出方式 可以使用任意變量名稱進行導入:const H = require('html-webpack-plugin')。
clean-webpack-plugin也可以從構造函數中傳參進行自定義設置
new CleanWebpackPlugin({
// 是否假裝刪除文件
// 如果為false則代表真實刪除,如果為true,則代表不刪除
dry:false,
// 是否將刪除日志打印到控制台 默認為false
verbose: true,
// 允許保留本次打包的文件
// true為允許,false為不允許,保留本次打包結果,也就是會刪除本次打包的文件
// 默認為true
protectWebpackAssets:true,
// 每次打包之前刪除匹配的文件
cleanOnceBeforeBuildPatterns:["*.html"],
// 每次打包之后刪除匹配的文件
cleanAfterEveryBuildPatterns:["*.js"],
})
-
dry:是否假裝刪除文件,官方文檔的描述是:Simulate the removal of files。
屬性可設置為:Boolean
此屬性當為 true 時,則不清空 /dist 目錄;當為 false 時,會清空 /dist 目錄
默認值: false
-
verbose:是否將刪除日志打印到控制台
屬性可設置為:Boolean
默認值: false
-
protectWebpackAssets:是否保留本次打包的文件
屬性可設置為:Boolean
屬性值為 false 時,本次打包文件也會被清除掉
默認值: true
-
cleanOnceBeforeBuildPatterns:設置打包之前刪除的文件
屬性可設置為:Array
此屬性類似一個鈎子,在打包執行之前,刪除此屬性匹配到的文件
默認值: ['**/*']
-
cleanAfterEveryBuildPatterns:設置打包之后刪除的文件
屬性可設置為:Array
此屬性與 cleanOnceBeforeBuildPatterns 類似,觸發時機是打包執行完畢后
默認值: []
使用clean-webpack-plugin時其實不需要配置屬性,默認就已經足夠使用。
🐋🐋 測試 clean-webpack-plugin 時,需要每次都修改 index.js 文件數據,因為 output 設置緩存,如果不修改源文件,不會重新打包
總結
🐋🐋🐋
- 打包器將開發環境代碼 編譯 為可部署環境代碼的 “編譯器”
- webpack運行在Node.JS環境中, 所以寫的 webpack配置項 其實是Node.JS 。
- webpack配置文件需要拋出一個模塊,導出模塊必須為 Object 或返回 Object 的函數
- webpack只提供一個 核心引擎 ,而其它功能使用 插件化 方式管理
- webpack是一個 JavaScript應用程序 ,默認只支持 JS模塊 的打包,對於 非JS模塊 需要使用 loader 轉換為 JS模塊
- 打包器不一定非要使用 Node.JS,也可以使用JAVA、.NET等任何一門語言
- 打包器其實就是 IO 操作,將entry(入口)文件數據讀取並經過一系列操作最終寫入到output(輸出)文件
如果此篇對您有所幫助,在此求一個star。項目地址: OrcasTeam/my-cli
本文參考
本文依賴
package.json
{
"name": "my-cli",
"version": "1.0.0",
"main": "index.js",
"author": "mowenjinzhao<yanzhangshuai@126.com>",
"license": "MIT",
"devDependencies": {
"clean-webpack-plugin": "3.0.0",
"html-webpack-plugin": "5.2.0",
"webpack": "5.24.0",
"webpack-cli": "4.5.0"
},
"dependencies": {
"jquery": "3.5.1"
},
"scripts": {
"start": "node",
"build": "webpack --config webpack.config.js"
}
}
webpack.config.js
const path = require('path')
const modules = {
// 入口文件
// 字符串形式
entry:path.join(__dirname, 'src/index.js'),
// 對象形式
// entry:{
// 'index':path.join(__dirname, 'src/index.js')
// },
// 輸出文件
// 字符串形式
// output:path.join(__dirname, 'dist/[name].js')
//對象形式
output:{
// 輸出文件的目錄地址
path:path.join(__dirname, 'dist'),
// 輸出文件名稱,contenthash代表一種緩存,只有文件更改才會更新hash值,重新打包
filename: '[name]_[contenthash].js'
},
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"],
})
]
}
// 使用node.js的導出,將配置進行導出
module.exports = modules