webpack是一個靜態模塊打包器,此處的模塊可以是任意文件,包括Sass、TypeScript、模板和圖像等。webpack可根據輸入文件的依賴關系,打包輸出瀏覽器可識別的JavaScript、CSS和HTML等文件,并且能對圖像做優化處理,如圖1所示。
:-: 
圖1 webpack打包
  目前,webpack的最新版本是4.33,其配置文件(webpack.config.js)的基本結構如下所示,包含了它的4個核心概念:入口(entry)、輸出(output)、加載器(loader)和插件(plugin)。
~~~
module.exports = {
entry: {}, //入口
output: {}, //輸出
module: { rules: [] }, //加載器
plugins: [] //插件
};
~~~
## 一、安裝
  如果要安裝最新版本的webpack,那么可以運行下面這條命令。
~~~
npm install --save-dev webpack
~~~
  當使用的是webpack 4+版本時,還需要再安裝它的命令行工具,安裝命令如下所示。
~~~
npm install --save-dev webpack-cli
~~~
  接下來就可以運行webpack的打包命令了,如下所示,其中“--config”參數后面會跟著配置文件的路徑。
~~~
npx webpack --config webpack.config.js
~~~
  通過package.json的scripts字段可聲明自定義的腳本任務(如下代碼所示),從而就能快捷的執行打包命令,例如“npm run build”。
~~~
{
scripts: {
build: "npx webpack --config webpack.config.js"
}
}
~~~
## 二、入口
  在webpack.config.js中,entry字段是一個入口,記錄著需要處理的模塊。從這個入口開始,webpack會遞歸地構建出模塊之間的依賴關系。
**1)單個入口**
  當entry字段是一個字符串類型時,其值就是模塊的相對或絕對路徑,如下所示。
~~~
module.exports = {
entry: "./index.js"
};
~~~
  這其實是一種簡寫,等價于下面的對象形式。對象的鍵就是chunk的名稱,[chunk](https://webpack.docschina.org/glossary)是webpack的特定術語,通常一個chunk對應或多個chunk組成一個bundle(即打包生成的文件)。
~~~
module.exports = {
entry: {
main: "./index.js"
}
};
~~~
  entry字段的值既可以是對象和字符串,也可以是由模塊路徑組成的數組,如下代碼所示,其中index.js和list.js兩個文件會被合并成一個chunk。
~~~
module.exports = {
entry: ["./index.js", "./list.js"]
};
~~~
**2)多個入口**
  只要在entry的對象中添加多個屬性就能設置多個入口,如下所示。
~~~
module.exports = {
entry: {
index: "./index.js",
list: __dirname + "/list.js"
}
};
~~~
  兩個chunk會被分別命名為index和list,其中\_\_dirname是Node.js內置的全局變量,保存著當前文件所處目錄的絕對路徑。
## 三、輸出
  在webpack.config.js中,output字段是一個對象,用于配置輸出的信息。它的filename屬性可聲明輸出的文件名,而另一個path屬性可配置輸出目錄的絕對路徑。與入口不同,在配置文件中只能存在一個輸出。
**1)filename**
  如果在入口中聲明了多個chunk,那么在配置輸出時得用占位符來表示對應的bundle名稱,如下所示。
~~~
module.exports = {
entry: {
index: "./index.js",
list: "./list.js"
},
output: {
filename: "[name].bundle.js"
}
};
~~~
  \[name\]表示chunk的名稱,例如index和list,還有另外三個常用的占位符,如表3所示。
:-: 
表3 filename中的占位符
  \[hash\]常與\[name\]配合使用(如下所示),在運行打包命令后,默認會在配置文件所處的位置創建dist目錄,并且把生成的bundle文件放置其中。
~~~
module.exports = {
output: {
filename: "[name].[hash].bundle.js"
}
};
~~~
**2)path**
  如果要更改默認的輸出路徑(即不想在dist目錄下生成bundle文件),那么可以通過設置path實現。但要注意,它的值必須是絕對路徑,不能是相對路徑,如下所示。
~~~
module.exports = {
output: {
path: __dirname + "/build"
}
};
~~~
## 四、加載器
  加載器(loader)能在webpack加載模塊時對其進行預處理,即對模塊的源碼進行轉換,下面列出加載器的幾個比較典型的用途。
  (1)將瀏覽器無法識別的JSX、Sass等語言轉換成JavaScript、CSS等語言。
  (2)把圖像轉換成Data URI格式嵌入到JavaScript文件中。
  (3)用ES6的import關鍵字將CSS文件導入到JavaScript中。
**1)配置**
  加載器不僅需要單獨安裝,還得在webpack.config.js中配置module字段。下面是一個簡單的配置示例,其作用是讓file-loader加載器處理四種類型的圖像。
~~~
module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [ "file-loader" ]
}]
}
};
~~~
  module的值是一個對象,包含一個rules屬性,記錄了模塊匹配的規則,而這些規則又會以對象的形式組成一個數組,作為rules屬性的值。每條規則必須包含test和use兩個屬性,前者是一個正則表達式,可設置匹配條件;后者是一個由字符串或對象組成的數組,可指定要使用的加載器。只有當test屬性中的條件匹配成功后,才會讓use屬性中的加載器處理相應的模塊。
  接下來將展示加載器的用法,以上面的file-loader為例,首先通過命令將其安裝,如下所示。
~~~
npm install --save-dev file-loader
~~~
  然后創建一個index.js文件,其內容就是引入一張avatar.jpg圖像,并將實例化的Image對象添加到頁面的元素內,如下所示。
~~~
import src from "./avatar.jpg";
var img = new Image();
img.src = src;
document.body.appendChild(img);
~~~
  再創建一張index.html頁面,引用打包生成的index.bundle.js文件,如下所示。
~~~
<body>
<script src="dist/index.bundle.js"></script>
</body>
~~~
  最后執行webpack的打包命令,就會在index.html的元素中添加下面的元素。
~~~
<img src="68fd51ab711118f323bdddf6de7a0175.jpg" />
~~~
  注意,默認情況下,圖像會隨著bundle文件一起被放置到dist目錄下。這樣的話,上述元素將無法讀取到圖像,得為其src屬性加上路徑。為了解決該問題,可以利用file-loader提供的配置參數。下面將use屬性中的規則修改成對象形式,用options參數記錄publicPath選項,即定義圖像的發布目錄。
~~~
module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: "file-loader",
options: { publicPath: "./dist" }
}]
}]
}
};
~~~
  當一個條件對應多個加載器時,其執行順序是從右到左。以下面的加載器為例,先執行url-loader,后執行file-loader。
~~~
module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [ "file-loader", "url-loader" ]
}]
}
};
~~~
  由于目前市面上的加載器有可能無法滿足實際需求,因此官方提供了自定義加載器的方法,具體可參考[相關文檔](https://www.webpackjs.com/contribute/writing-a-loader/)。
**2)Babel**
  在之前的[第2篇](https://www.cnblogs.com/strick/p/10984632.html)中,對Babel做了專門的講解,現在利用加載器可以將Babel的配置寫到webpack.config.js中,接下來將演示在webpack中使用Babel。
  首先安裝babel-loader、Babel的核心包以及集合了ES語法的預設,命令如下所示。
~~~
npm install --save-dev babel-loader @babel/core @babel/preset-env
~~~
  然后在webpack.config.js的module字段中配置規則,如下所示。
~~~
module.exports = {
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}]
}]
}
};
~~~
  rules中的exclude屬性定義了忽略的條件,即不會對node\_modules目錄中的腳本文件執行babel-loader。在babel-loader的presets選項中聲明了所使用的預設。當執行打包命令后,就會對下面這樣的ES6語法進行編譯,轉換成低版本的ES語法。
~~~
let fn = () => true;
~~~
## 五、插件
  插件能夠借助webpack引擎的能力,將自定義的行為注入到webpack的構建流程中,解決加載器無法實現的功能,例如分離打包、壓縮文件等。插件不僅能處理模塊和編譯過的資源,還能監控文件的變化。與加載器一樣,插件也可根據特定需求實現自定義,具體可參考[官方文檔](https://www.webpackjs.com/contribute/writing-a-plugin/)。
  插件的配置被放在了plugins字段中,它的值是一個由插件實例組成的數組。接下來將通過html-webpack-plugin來演示插件的用法。
**1)配置**
  html-webpack-plugin不僅能根據模板生成一個HTML文件,還能自動引入所需的bundle文件,對于那些隨著編譯而發生名稱變化的bundle文件特別有用。
  要使用它,首先需要將其安裝,相關的命令如下所示。
~~~
npm install --save-dev html-webpack-plugin
~~~
  然后在webpack.config.js中聲明插件的實例,如下配置所示,在初始化插件時,沒有為其傳遞任何參數。
~~~
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: "./index.js"
},
output: {
filename: "[name].[hash].bundle.js"
},
plugins: [
new HtmlWebpackPlugin()
]
};
~~~
  最后執行打包命令,生成的HTML文件如下所示,其中腳本文件的名稱包含了一個hash值。
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="index.4ee657c406f9babd171a.bundle.js"></script>
</body>
</html>
~~~
**2)自定義模板**
  除了上面所使用的默認模板之外,html-webpack-plugin還提供了自定義模板的功能。默認情況下,html-webpack-plugin采用的是EJS模板引擎,聲明的加載器是ejs-loader。如果要使用其它模板引擎,那么必須得把相應的加載器添加到配置文件中。
  下面利用[EJS模板的語法](https://ejs.co/#docs)插入頁面標題,首先創建一個template.html模板文件,如下所示。
~~~
<!DOCTYPE html>
<html>
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
<meta charset="utf-8" />
</head>
<body>
<div>模板內容</div>
</body>
</html>
~~~
  然后向插件傳遞兩個參數:title和template,前者就是頁面標題,后者是模板路徑。如此設置之后,就能渲染出所需要的頁面了。
~~~
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: "模板",
template: "./template.html"
})
]
};
~~~
## 六、模塊化
  webpack實現了一套兼容所有模塊化方案的機制,這讓任意文件皆有可能成為模塊,而構建依賴圖和按需打包等功能則都由webpack內部完成。
  在webpack中,能夠表達模塊之間依賴關系的方式有多種,例如下面所列的。
  (1)CommonJS規范的require()函數。
  (2)AMD規范的define()函數。
  (3)ES6標準的import語句。
  (4)Sass和Less中的@import語句。
  (5)CSS中引用圖像的url()函數。
  (6)元素用于加載圖像的src屬性。
*****
> 原文出處:
[博客園-前端利器躬行記](https://www.cnblogs.com/strick/category/1472499.html)
[知乎專欄-前端利器躬行記](https://zhuanlan.zhihu.com/pwtool)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020