廣州藍(lán)景實(shí)訓(xùn)部分享--Webpack 基礎(chǔ)教學(xué)
至今,有很多小伙伴們都不太了解Webpack打包工具,今天廣州藍(lán)景的小編跟大家普及下技術(shù)點(diǎn)的教學(xué),請(qǐng)大家收藏好哦
前言
眾所周知,前端項(xiàng)目使用 JS 只能通過在 HTML 文件中使用<script>標(biāo)簽:
<script>
console.log('hello');</script><script src="./index.js"></script>
JS 原本的設(shè)計(jì)初衷只是為了解決一些簡(jiǎn)單的網(wǎng)頁(yè)交互(比如:表單驗(yàn)證),可設(shè)計(jì)者做夢(mèng)也想不到,JavaScript 的發(fā)展非??欤茏龅氖虑橐苍絹碓蕉?,網(wǎng)頁(yè)的代碼量也對(duì)越來越多。這個(gè)時(shí)候如果將所有的 JS 代碼都放在<script>標(biāo)簽中或者存放到一個(gè) JS 文件中,就會(huì)使得我們的代碼看起來非常臃腫。首先出現(xiàn)的做法就是分文件,通過命名不同 JS 文件將不同功能的 JS 代碼存放到對(duì)應(yīng)的文件中。這樣的話就能解決所有的代碼都堆積在一起,造成代碼臃腫。但是這樣的做法同樣也存在問題:
1.瀏覽器同域名請(qǐng)求的最大并發(fā)數(shù)限制。HTTP 客戶端一般對(duì)同一服務(wù)器的并發(fā)連接個(gè)數(shù)都是有限制(不同瀏覽器的限制個(gè)數(shù)可能存在不同),如果遇到有的 JS 文件處理的任務(wù)耗時(shí)過長(zhǎng)、網(wǎng)絡(luò)速度不佳等情況,那么后面的其余文件就只能等待前面文件加載完成,造成網(wǎng)頁(yè)加載速度慢,出現(xiàn)白屏情況。
2.全局環(huán)境污染。通過<script>標(biāo)簽鏈入 JS 文件,變量都會(huì)存在全局環(huán)境中,造成變量重名、污染全局環(huán)境等情況。
為了解決全局污染的問題,我們會(huì)采用下面兩種方法:
·?使用自執(zhí)行函數(shù)(IIFE)
·?ES moudle
let a = (function () {
let a = 'hello JS';
return {
b: 'IIFE',
c: 'IIFE',};})();
通過自執(zhí)行函數(shù)創(chuàng)建函數(shù)作用域,可以防止變量污染全局環(huán)境。然后將需要使用的變量通過 return 的方式返回給外面使用。但是這樣同樣會(huì)存在問題,當(dāng)項(xiàng)目很大時(shí),我們使用了很多文件來編寫不同功能的代碼,然后再將文件引入到 HTML 中。當(dāng)項(xiàng)目中需要通過某個(gè)變量來定位問題,那么這個(gè)時(shí)候我們很難去定位到這個(gè)全局變量存在于那個(gè) JS 文件中,因?yàn)槟K之間的依賴關(guān)系并不是很清晰,而且不同模塊之間還是存在變量名重復(fù)的問題。
隨著網(wǎng)頁(yè)代碼越來越龐大,JavaScript 模塊化編程就變得越來越迫切了。在 ES2015 屬于 JavaScript 的模塊化寫法終于出現(xiàn)。
// a.jsimport { b } from './b.js';// b.jsexport const b = 'ES module';
通過export去暴露變量,然后通過import去引入變量。通過這樣可以清楚的知道變量來自于哪個(gè)模塊,也不會(huì)存在命名沖突的問題,污染全局環(huán)境等問題。雖然 ES module 在不同瀏覽器的支持程度不一,但是我們可以通過 babel 將 ES6+代碼轉(zhuǎn)成 ES5,用來兼容不同的瀏覽器。
使用模塊化開發(fā)項(xiàng)目可以方便我們維護(hù)代碼,定位項(xiàng)目問題。但是模塊化開發(fā)也會(huì)增加需要引入文件的數(shù)量,那么模塊化會(huì)出現(xiàn)請(qǐng)求并發(fā)數(shù)限制的問題,造成項(xiàng)目加載慢,首屏白屏?xí)r間過長(zhǎng)。這個(gè)時(shí)候我們就需要一個(gè)工具在項(xiàng)目上線時(shí)幫助我們自動(dòng)化執(zhí)行文件合并、轉(zhuǎn)譯ES6+代碼、優(yōu)化項(xiàng)目性能。在眾多打包工具中,webpack無疑是現(xiàn)在最耀眼的。
正文
本文相關(guān)代碼支持webpack4、webpack5,特定版本寫法會(huì)在對(duì)應(yīng)位置寫明。

本質(zhì)上,webpack是一個(gè)用于現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包工具。當(dāng) webpack 處理對(duì)應(yīng)程序時(shí),它會(huì)在內(nèi)部構(gòu)建一個(gè)依賴圖,此依賴圖對(duì)應(yīng)映射到項(xiàng)目所需的每個(gè)模塊,并生成一個(gè)或多個(gè) bundle。
以上是 webpack 官方文檔中概念一章對(duì)于 webpack 的描述,描述當(dāng)中提到 webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包工具,這里的靜態(tài)模塊不單單指 ES module。在 webpack 中的模塊可以是以下幾種類型:
ES2015?
import
語(yǔ)句。CommonJS?
require()
語(yǔ)句。AMD?
define
和require
語(yǔ)句。css/sass/less 文件中的
@import
語(yǔ)句。stylesheet
url()
或者 HTML<img src="">
文件中的圖片鏈接。
依賴圖:當(dāng) webpack 處理應(yīng)用程序時(shí),它會(huì)根據(jù)命令行參數(shù)中或配置文件中定義的模塊列表開始處理。從入口開始,webpack 會(huì)遞歸的構(gòu)建一個(gè)依賴關(guān)系關(guān)系圖,這個(gè)依賴圖包含著應(yīng)用程序中所需的每個(gè)模塊,然后將所有模塊打包為少量的 bundle。
Bundle:bundle 由許多不同的模塊生成,包含已經(jīng)經(jīng)過加載和編譯過程的源文件的最終版本。
chunk:在打包過程中,模塊會(huì)被合并成chunk,chunk合并成chunk 組。chunk 組為webpack 配置文件中指定的entry字段,每個(gè)入口對(duì)應(yīng)一個(gè)chunk 組。而chunk有兩種形式:
initial(初始化)
是入口起點(diǎn)的 main chunk。此 chunk 包含為入口起點(diǎn)指定的所有模塊及其依賴項(xiàng)。non-initial
是可以延遲加載的塊??赡軙?huì)出現(xiàn)使用動(dòng)態(tài)導(dǎo)入或者SplitChunksPlugin時(shí)。
chunk 對(duì)應(yīng)的是打包過程中,對(duì)各個(gè)依賴模塊進(jìn)行捆綁的過程。而 bundle 指的是編譯完成生成的最終版本。
簡(jiǎn)單的了解了 webpack 的相關(guān)知識(shí),接下來就進(jìn)入到 webpack 使用的相關(guān)學(xué)習(xí):
1.安裝 webpack
mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev
webpack-cli
:用于在命令行中運(yùn)行 webpack
2.配置入口(entry)和出口(output)
在項(xiàng)目根目錄中創(chuàng)建 src 文件夾、webpack.config.js
- webpack-demo ?
? | - src ? ?
? | - webpack.config.js ? ?
? | - package.json
配置 webpack 配置文件(webpack.config.js)
// webpack.config.jsconst path = require('path');module.exports = {
?entry: './src/main.js',
?output: {
? ?path: path.resolve(__dirname, 'dist'),
? ?filename: 'js/bundle.js',
?},};
通過 module.exports 暴露一個(gè)對(duì)象,對(duì)象中的不同鍵名用來指定不同 webpack 配置。entry
用于指定 webpack 編譯時(shí)的入口文件,webpack 會(huì)以入口文件作為起點(diǎn),生成項(xiàng)目依賴圖,用來打包項(xiàng)目。默認(rèn)值為./src/index.js
。output 用于指定項(xiàng)目編譯文件的輸出位置。output
最少需要配置 filename 屬性,filename 屬性用于指定編譯后的 bundle 文件的文件名。path 用于指定編譯后文件輸出的位置。默認(rèn)值為./dist
上面代碼中的 path 是 node 中處理文件和目錄的路徑,因?yàn)?output 的 path 需要一個(gè)絕對(duì)路徑,所以這里使用到 node path 的 resolve 方法,這個(gè)方法可以將路徑片段處理成一個(gè)絕對(duì)路徑。其中的__dirname指的是對(duì)應(yīng) js 文件在被調(diào)用時(shí)所處的絕對(duì)路徑,由于 webpack.config.js 處于項(xiàng)目根目錄中,那么最后會(huì)將編譯好的文件生成到根目錄下的 dist 文件夾中,如果根目錄中沒有 dist 文件夾,則會(huì)創(chuàng)建該文件夾。
創(chuàng)建 main.js 文件
// src/main.jsconsole.log('Hello webpack!');
在 package.json 中配置 npm 命令:
{
? "scripts": {
? ? "build": "webpack"
?}}
在命令行中運(yùn)行命令
npm run build
通過執(zhí)行 build 命令運(yùn)行 webpack-cli,webpack 默認(rèn)命令為 build 命令(可以省略),build 命令會(huì)調(diào)用 webpack 完成項(xiàng)目的編譯。
_編譯完成后會(huì)在根目錄生成一個(gè)dist
文件夾,文件夾中一個(gè)js
文件夾里面包含生成的bundle.js
。在項(xiàng)目根目錄下新建一個(gè) public 文件夾,在該文件夾中創(chuàng)建index.html
_。然后將bundle.js
引入到 html 文件中,打開到瀏覽器,這時(shí)候在控制臺(tái)可以看到輸出的“Hello webpack!”;
上面示例代碼中我們只給entry配置了一個(gè)入口,常見的單入口項(xiàng)目我們稱之為單頁(yè)應(yīng)用(SPA),例如:vue-cli 的開發(fā)模式就是單頁(yè)應(yīng)用。如果我們想配置多入口:多頁(yè)應(yīng)用(MPA),我們可以使用對(duì)象的寫法:
// webpack.config.jsconst path = require('path');module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js',},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',},};// src/index.jsconsole.log('index.js');
// src/index.js
console.log('index.js');
重新執(zhí)行 build 命令:
npm run build
運(yùn)行命令之后會(huì)在 dist 生成兩個(gè)新文件。上面我們修改了 webpack 的配置文件,將 entry 賦值為對(duì)象,對(duì)象中的鍵(key)是 chunk 的名稱,值描述了 chunk 的文件路徑。由于使用多入口,所以我們輸出文件的文件名不應(yīng)該固定一個(gè),否則在打包過程會(huì)報(bào)錯(cuò)。webpack 提供了一種稱為 substitution(可替換模板字符串)的方式,通過帶括號(hào)字符串來模板化文件名。上面的[name]
:為此 chunk 的名稱,否則使用 chunk 的 ID。[contenthash]
:此 chunk 的 hash 值,值根據(jù)資源內(nèi)容創(chuàng)建出唯一的 hash。
瀏覽器使用一種名為緩存的技術(shù)??梢酝ㄟ^命中緩存,以降低網(wǎng)絡(luò)流量,使網(wǎng)站加載速度更快,然而如果在部署新版本時(shí)不更改資源的文件名,瀏覽器可能會(huì)認(rèn)為他沒有被更新,就會(huì)使用它的緩存版本。使用[contenthash]
可以根據(jù)資源內(nèi)容創(chuàng)建出唯一的 hash,當(dāng)內(nèi)容改變時(shí),文件名會(huì)對(duì)應(yīng)做出改變,避免瀏覽器使用緩存版本。
3.配置常用 loader
webpack 只能處理 JavaScript 和 JSON 文件,這是 webpack 開箱可用的自帶能力。loader 讓 webpack 能夠去處理其他類型的文件,并將它們轉(zhuǎn)換為有效的模塊,以供應(yīng)用程序使用,以及被添加到依賴圖中。
3.1 處理 CSS
創(chuàng)建 css 樣式文件,并將樣式文件引入到 main.js 中:
/*src/assets/style.css*/html,body {
margin: 0;
padding: 0;}body {
background-color: skyblue;}
// src/main.jsimport './assets/css/style.css';console.log('Hello webpack!');// webpack中能通過import引入css和scss文件
由于 webpack 本身不自帶處理 css 文件的能力,所以需要下載對(duì)應(yīng) loader 處理 css 文件,將 css 文件轉(zhuǎn)換成 webpack 能夠處理的模塊。
npm install style-loader css-loader --save-dev
style-loader
:把 css 插入到 DOM 中。css-loader
:對(duì)@import 和 url()進(jìn)行處理,跟 js 解析 import/require()一樣。
在 webpack.config.js 中配置 css 解析規(guī)則:// webpack.config.jsconst path = require('path');module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js',},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',},
module: {
rules: [
? ?{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
? ?},
?],},};
module 選項(xiàng)決定了如何處理項(xiàng)目中的不同類型的模塊。rules 用于創(chuàng)建不同類型模塊的處理規(guī)則。test 選項(xiàng)用于引入所有通過斷言測(cè)試的模塊,然后 use 選項(xiàng)決定了通過斷言測(cè)試的模塊使用那些 loader 進(jìn)行處理,loader 可以通過傳入多個(gè)已達(dá)到鏈?zhǔn)秸{(diào)用的效果,loader 的調(diào)用順序?yàn)椋簭挠业阶?從下到上),每個(gè) loader 將自己處理的結(jié)果返回給接下來要處理的 loader。上面示例代碼中,css-loader 在編譯時(shí),用來處理 main.js 中通過引入 css 文件,然后將處理結(jié)果返回給 style-loader,style-loader 可以將處理的 css 代碼插入到 DOM 中。
module.exports = {
module: {
rules: [
? ?{
test: /\.css$/i,
use: ['style-loader'],
? ?},
? ?{
test: /\.css$/i,
use: ['css-loader'],
? ? ?},
? ?],
?},};// 以上代碼與上面的代碼效果一樣,loader的解析順序?yàn)閺挠业阶蠡蛘邚南碌缴稀?/span>
配置完 css 處理規(guī)則后,重新運(yùn)行打包命令,然后將打包出來的文件引入到 index.html 中,可以看到樣式通過 style 標(biāo)簽已經(jīng)被引入到頁(yè)面中,webpack 通過 css-loader 和 style-loader 能夠正確的處理 css 文件。
3.2 處理 scss|sass
如果在項(xiàng)目開發(fā)中想使用 scss|sass,那么我們需要安裝sass-loader
,由于sass-loader
依賴于Dart Sass
或Node Sass
,推薦使用Dart Sass
。
npm install sass-loader sass --save-dev
dart sass:npm install sass --save-dev
node sass: npm install node-sass --save-dev
sass-loader
:加載 Sass/SCSS 文件并將它們編譯為 CSS
/*src/assets/scss/style.scss*/$bgcolor: skyblue;* {
margin: 0;
padding: 0;}body {
background-color: $bgcolor;}
// src/main.jsimport './assets/css/style.scss';console.log('Hello webpack!');
修改 webpack.config.js:
// webpack.config.jsconst path = require('path');module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js',},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',},
module: {
rules: [
? ?{
test: /\.(css|scss|sass)$/i,
use: ['style-loader', 'css-loader', 'sass-loader'],
? ? ?},
? ?],
?},};
如果你使用的是less,那么你可以使用less-loader
。如果你使用的是stylus,那么你可以使用stylus-loader
。
3.3 處理圖片資源
在 webpack5 之前的版本,webpack 使用file-loader
和url-loader
來處理圖像資源。
url-loader
:將文件作為 data URL 內(nèi)聯(lián)到 bundle 中file-loader
:將文件發(fā)送到輸出目錄
找一張圖片放到 src/assets/images 文件夾,修改 style.scss
// src/assets/css/style.scss$bgcolor: skyblue;* {
margin: 0;
padding: 0;}body {
width: 100vw;
height: 100vh;
background-color: $bgcolor;
background-image: url('../images/1.jpg');
background-size: cover;
background-repeat: no-repeat;}
修改 webpack.config.js
// webpack.config.js// 添加圖像資源處理規(guī)則module.exports = {
module: {
rules: [
? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
use: ['file-lodaer'],
? ? },
? ],
},};
配置完畢完,重新執(zhí)行 build 命令。編譯完成后可以看到在 scss 文件中引入的圖像資源生成在 dist 文件夾下。file-loader 會(huì)將資源發(fā)送到輸出目錄,并將輸出之后的正確路徑返回給原本的位置進(jìn)行替換。
我們還可以通過給file-loader配置options選項(xiàng)來配置圖像資源的輸出路徑和文件名
// webpack.config.jsmodule.exports = {
module: {
rules: [
? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images',
? ? ? ?},
? ? ?},
? ?],
?},};
更改 file-loader 的配置選項(xiàng),提供options.name用來自定義輸出文件名,[name]
為圖像原本的文件名,[ext]
為圖像原本的后綴名。options.output用于設(shè)置file-loader輸出的文件將放置在那個(gè)路徑下,實(shí)例代碼中圖像最終的輸出路徑為dist/images
file-loader相當(dāng)于復(fù)制資源文件到輸出目錄中,然后將對(duì)應(yīng)的引入的圖像資源路徑解析為正確的路徑。而url-loader則是將圖像資源默認(rèn)處理成base64 URL
我們來修改 webpack.config.js 中圖像的處理規(guī)則:
// webpack.config.jsmodule.exports = {
module: {
rules: [
? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
use: ['url-loader'],
? ?},
?],},};
重新運(yùn)行 build 命令,將編譯完成的 js 文件引入到 index.html 中??梢钥吹奖尘皥D片的 url 生成了一串 base64 的字符串。如果資源圖像體積過大,那么這個(gè)時(shí)候就不適合使用url-loader來生成 base64 字符串了。所以我們需要給url-loader設(shè)置一個(gè)資源體積限制,只有小于這個(gè)限制才會(huì)將圖像資源轉(zhuǎn)換為 base64 字符串。
// webpack.config.jsmodule.exports = {
module: {
rules: [
? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'url-loader',
options: {
limit: 1024 * 10,
? ? ?},
? ?},
?],},};
通過給url-loader
設(shè)置 options.limit 選項(xiàng),可以限制url-loader
能夠處理的資源圖像大小。limit 的單位為 B(字節(jié)),上面示例代碼中,我們?cè)O(shè)置如果圖像資源大于等于 10KB 時(shí)將不會(huì)使用url-loader
將圖像資源處理成 base64 字符串,而是會(huì)使用file-loader
輸出圖像資源到輸出目錄中,并解析出對(duì)應(yīng)路徑。如果沒有下載file-loader
,在圖像超過 limit 限制時(shí),會(huì)報(bào)錯(cuò)找不到file-loader
。
在 webpack5 中,我們處理圖像資源不再使用file-loader
和url-loader
,而是使用內(nèi)置的Asset Modules
替換原本的 loader。
asset/resource
?發(fā)送一個(gè)單獨(dú)的文件并導(dǎo)出 URL。之前通過使用?file-loader
?實(shí)現(xiàn)。asset/inline
?導(dǎo)出一個(gè)資源的 data URI。之前通過使用?url-loader
?實(shí)現(xiàn)。asset
?在導(dǎo)出一個(gè) data URI 和發(fā)送一個(gè)單獨(dú)的文件之間自動(dòng)選擇。之前通過使用?url-loader
,并且配置資源體積限制實(shí)現(xiàn)。
// webpack.config.jsmodule.exports = {
module: {
rules: [
? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
? ?},
?],},};
在上面示例代碼中,通過type: ‘a(chǎn)sset’替換 webpack5 之前使用url-loader
和配置limit選項(xiàng)達(dá)到一樣的效果。type: ‘a(chǎn)sset’會(huì)在type: asset/resource和type: asset/inline之間進(jìn)行選擇,默認(rèn)小于 8kb 的文件,會(huì)使用type: asset/inline,否則使用type: asset/resource。如果想修改文件大小限制,可以修改Rule.parser.dataUrlCondition.maxSize
// webpack.config.jsmodule.exports = {
module: {
rules: [
? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
generator: {
filename: '[name].[ext]',
? ? ?},
parser: {
dataUrlCondition: {
maxSize: 1024 * 10,
? ? ? ?},
? ? ?},
? ?},
?],},};
通過添加 parser.dataUrlCondition.maxSize 來更改默認(rèn)的 8kb 限制。generator.filename 為設(shè)置文件輸出名。
3.4 轉(zhuǎn)義 ES6+代碼
由于各個(gè)瀏覽器的不同版本對(duì) ECMA script 語(yǔ)法的支持程度不一,所以我們?cè)诰幾g代碼時(shí)會(huì)使用 babel 將 ES6+代碼編譯成 ES5 代碼,以兼容不同瀏覽器的不同版本。
npm install babel-loader @babel/core @babel/preset-env --save-dev
babel-core?:把 js 代碼分析成 ast ,方便各個(gè)插件分析語(yǔ)法進(jìn)行相應(yīng)的處理
babel-preset-env:將現(xiàn)代 JS 編譯為 ES5
**babel-loader **:用于 webpack
配置規(guī)則:
// webpack.config.jsmodule.exports = {
module: {
rules: [
? ?{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
? ? ? ?},
? ? ?},
// 排除node_modules模塊中的js,避免浪費(fèi)性能。 exclude: /node_modules/,
? ?},
?],},};
配置完上述規(guī)則,我們?cè)?main.js 中輸入一段箭頭函數(shù)的代碼,看看編譯后 babel 會(huì)如何處理我們的箭頭函數(shù)。
我們可以在編譯完成的 js 中看到原本的箭頭函數(shù)已經(jīng)改成了 ES5 的 function 形式。
4.配置常用 plugins
loader 用于轉(zhuǎn)換某些類型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務(wù)。包括:打包優(yōu)化,資源管理,注入環(huán)境變量。使用一個(gè)插件時(shí),需要require()
對(duì)應(yīng)插件,然后可以通過 new 操作符創(chuàng)建一個(gè)實(shí)例,將這個(gè)實(shí)例添加到 plugins 數(shù)組中。
ref="http://webpack.luxiangfan.club/#/?id=_41-htmlwebpckplugin">4.1 HtmlWebpckPlugin
HtmlWebpckPlugin 可以幫助我們?cè)诿看未虬戤呏?,將打包完?js 文件引入到 html 文件中,從而不需要每次打包都手動(dòng)引入文件。
npm install html-webpack-plugin --save-dev
// webpack.config.jsconst HtmlWebpackPlugin = require('html-webpack-plugin');let chunks = ['main', 'index'];let htmlPlugins = chunks.map((ele) => {
return new HtmlWebpackPlugin({
template: './public/index.html', // 生成html的模板文件,可以在模板中使用<%= htmlWebpackPlugin.options.title%>引入配置中的title字段,指定html的title的內(nèi)容。 favicon: './public/favicon.ico', // 指定生成html文件中引入該ico圖標(biāo) title: 'Hello webpack', // 標(biāo)題 filename: `${ele}.html`, // HTML文件名 chunks: [ele], // 該plugin實(shí)例生成的HTML需要引入的chunk組});});module.exports = {
plugins: [...htmlPlugins],};
<!-- public/index.html--><!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= htmlWebpackPlugin.options.title%></title>
</head>
<body></body></html>
上面示例代碼中,由于在一開始我們配置了多入口,所以需要?jiǎng)?chuàng)建對(duì)應(yīng)數(shù)量的htmlwebpackplugin實(shí)例,通過配置對(duì)象中的chunk選項(xiàng)來確定每個(gè)實(shí)例需要引入的chunk組。
ref="http://webpack.luxiangfan.club/#/?id=_42-cleanwebpackplugin">4.2 cleanWebpackPlugin
由于每次運(yùn)行 build 命令都會(huì)生成名字不同的 bundle 文件,所以會(huì)導(dǎo)致 dist 文件夾非?;靵y。所以使用clean-webpack-plugin
在打包之前清除清除 dist 文件夾中的內(nèi)容。
npm install clean-webpack-plugin --save-dev
// webpack.config.jsconst { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = {
plugins: [new CleanWebpackPlugin()],};
現(xiàn)在重新執(zhí)行 build 命令,dist 文件夾中就只會(huì)存在本次編譯的文件。
5.devServer
在每次編譯代碼時(shí),手動(dòng)運(yùn)行 npm run build 會(huì)顯得很麻煩,不利于開發(fā)。webpack 提供了webpack-dev-server
這個(gè)工具用來幫助我們?cè)诖a發(fā)生變化后,自動(dòng)編譯代碼,并且刷新瀏覽器。
首先我們需要下載webpack-dev-server
這個(gè)工具
npm install webpack-dev-server --save-dev
配置 serve 命令:
// package.json{
"scripts": {
"serve": "webpack serve --mode=development --open"}}
通過 cli 的方式設(shè)置 webpack 的 mode 為 development,關(guān)于 mode 下面會(huì)講到。--open:將開啟服務(wù)的網(wǎng)址在默認(rèn)瀏覽器上打開。
運(yùn)行npm run serve
,webpack-dev-server會(huì)開啟服務(wù),服務(wù)的地址會(huì)打開到默認(rèn)瀏覽器中。接下來修改項(xiàng)目中的代碼,webpack 會(huì)自動(dòng)重新編譯,編譯完成后刷新頁(yè)面。
注意:webpack-dev-server并不會(huì)將編譯好的文件寫入到任何輸出目錄,而是將 bundle 文件保留在內(nèi)存中,然后將它們 serve 到 server 中。可以在服務(wù)開啟的網(wǎng)址后輸入webpack-dev-server,就可以看到服務(wù)文件的位置。例如:http://localhost:8080/webpack-dev-server
webpack 提供了devServer選項(xiàng),用來改變webpack-dev-server的行為。
在 webpack.config.js 中添加 devServer 相關(guān)配置:
// webpack.config.jsmodule.exports = {
? ?devServer: {
? ?open: true, // 自動(dòng)打開服務(wù)網(wǎng)址到默認(rèn)瀏覽器,可以像上面在cli中設(shè)置,也可以在devServer中設(shè)置。除了Boolean,也可以是string,例如 'Chrome' ? ?host: '0.0.0.0',// 指定host,默認(rèn)為'localhost',如果想讓局域網(wǎng)內(nèi)可訪問,可設(shè)置為‘0.0.0.0’,這樣通過localhost和ip地址、127.0.0.1都能訪問的到 ? ? hot: true, // 開啟模塊熱更新,當(dāng)代碼更改時(shí),只需要更新對(duì)應(yīng)內(nèi)容,而不是刷新整個(gè)網(wǎng)頁(yè) ? ? hotOnly: true, // 啟用熱模塊替換 ? ? port: 8080 // 設(shè)置端口號(hào),默認(rèn)為:'8080' ? ? ?// 設(shè)置反向代理 ? ? ?proxy: {
? ? ? ? ? ? ? ? ? '/api': {
? ? ? ? ? ? ? target: 'http://localhost:3000',
? ? ? ? ? ? ? pathRewrite: {
? ? ? ? ? ? ? ? ? ?'^/api': ''
? ? ? ? ? ? ?}
? ? ? ? ?}
? ? ?}
?}}
6.開發(fā)模式(development)|生產(chǎn)模式(production)
開發(fā)環(huán)境和生產(chǎn)環(huán)境這兩個(gè)環(huán)境下的構(gòu)建目標(biāo)存在差異。開發(fā)環(huán)境中,需要強(qiáng)大的 source map 和一個(gè)有著 live reloading 或 hot module replacement 能力的 localhost server。而生產(chǎn)環(huán)境目標(biāo)則轉(zhuǎn)移至其它方面,例如壓縮 bundle,輕量的 source map,資源優(yōu)化等。所以通常我們都會(huì)將 webpack 的配置以環(huán)境作為區(qū)分。
在項(xiàng)目根目錄中新建build
文件夾,文件中新建三個(gè)js
文件,分別是webpack.base.conf.js
、webpack.dev.conf.js
、webpack.prod.conf.js
webpack.base.conf.js
:配置生產(chǎn)環(huán)境與開發(fā)環(huán)境共用配置,例如:入口,常用 loader 等webpack.dev.conf.js
:配置開發(fā)環(huán)境配置,例如:devServer、mode,source map 等webpack.prod.conf.js
:配置生產(chǎn)環(huán)境配置,例如:cleanWebpackPlugin,mode,資源優(yōu)化等
在區(qū)分環(huán)境時(shí),需要下載webpack-merge
工具,該用具是用于合并不同文件的 webpack 配置。
npm install webpack-merge --save-dev
除了webpack-merge
,還需要下載一些其它工具:
npm install cross-env postcss-loader postcss autoperfixer mini-css-extract-plugin css-minimizer-webpack-plugin uglifyjs-webpack-plugin --save-dev
cross-env
:該插件用于在不同平臺(tái)設(shè)置或使用正確的環(huán)境變量postcss-loader
:使用 postcss 處理 css 的 loaderpostcss
:postcss 是一個(gè)允許使用 JS 插件轉(zhuǎn)換樣式的工具。例如:添加樣式前綴、使用先進(jìn)的 css 語(yǔ)法等autoprefixer
:為樣式添加不同瀏覽器前綴mini-css-extract-plugin
:將 css 提取到單獨(dú)的文件中。css-minimizer-webpack-plugin
:使用 cssnano 優(yōu)化和壓縮 CSS。uglifyjs-webpack-plugin
:使用 uglify-js 壓縮 JavaScript。
配置webpack.base.conf.js
:
// build/webpack.conf.jsconst path = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 提取css樣式到單獨(dú)的css文件中const HtmlWebpackPlugin = require('html-webpack-plugin');const htmlPlugins = ['main', 'index'].map((ele) => {
return new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico',
title: 'Hello webpack',
filename: `${ele}.html`,
chunks: [ele],});});// 在生產(chǎn)環(huán)境下添加MiniCssExtractPluginconst plugins =
process.env.NODE_ENV === 'production'
? [...htmlPlugins, new MiniCssExtractPlugin()]
?: [...htmlPlugins];module.exports = {
entry: {index: './src/index.js',
main: './src/main.js'
?},
output: {
path: path.resolve(__dirname,'dist'),
filename: '[name].[contenthash].bundle.js'
?},
module: {rules: [
? ? ? ? ?{
test: /\.(css|scss|sass)$/i,
use: [
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
? ? ? ? ? ? ?]
? ? ? ? ?},
? ? ? ? ? ?{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
generator: {
filename: '[name].[ext]',
? ? ? ? ? ? ?},
parser: {
dataUrlCondition: {
maxSize: 1024 * 8,
? ? ? ? ? ? ? ?},
? ? ? ? ? ? ?},
? ? ? ? ? ?},
?{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
? ? ? ? ? ? ? ?},
? ? ? ? ? ? ?},
// 排除node_modules模塊中的js,避免浪費(fèi)性能。 exclude: /node_modules/,
? ? ? ? ? ?},
? ? ?]
?},
plugins: plugins,
resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
assets: path.resolve(__dirname, '../src/assets'),
? ? ?},
extensions: ['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm'],
? ? ?},}
在webpack.base.conf.js
中修改了樣式文件的處理規(guī)則,首先在生產(chǎn)環(huán)境下使用MiniCssExtractPlugin.loader
提取 css 樣式文件到單獨(dú)的 css 文件中,因?yàn)橥ㄟ^style-loader
添加樣式需要等待 js 文件加載完畢之后,通過 js 操作 DOM 生成style
標(biāo)簽再將樣式添加到標(biāo)簽中。所以當(dāng) js 加載被阻塞或網(wǎng)速慢時(shí),那么樣式就沒辦法及時(shí)生效,使得頁(yè)面布局混亂。然后在css-loader
處理之前添加了postcss-loader
,該 loader 可以用來為樣式添加瀏覽器前綴或使用還未被瀏覽器支持的樣式等。這里需要在根目錄新建postcss.config.js
,postcss
默認(rèn)會(huì)使用根目錄下的該文件作為配置項(xiàng)。在共用配置還新增了resolve
配置項(xiàng),resolve.alias
用于配置路徑別名,為常用的路徑配置別名。resolve.extensions
用于配置忽略文件后綴時(shí),webpack 會(huì)以定義的resolve.extensions
按順序嘗試解析,如果多個(gè)文件名相同,但是后綴不同,webpack 會(huì)以數(shù)組中找到的第一個(gè)后綴的文件作為解析對(duì)象,其余會(huì)跳過。
注意:process 是 node 中一個(gè)全局變量,用于提供當(dāng)前進(jìn)程相關(guān)信息。process.env 返回一個(gè)包含用戶環(huán)境信息的對(duì)象。NODE_ENV 并不是 env 本身存在的一個(gè)屬性,而是一個(gè)自定義屬性,在前端工程化中大家約定俗成使用該屬性設(shè)置或獲取環(huán)境變量。由于在不同系統(tǒng)環(huán)境下設(shè)置屬性會(huì)存在差異,為了兼容不同平臺(tái),我們會(huì) npm 命令中使用cross-env
跨平臺(tái)設(shè)置環(huán)境變量。
// postcss.config.js// autoprefixer:用于添加樣式前綴module.exports = {
plugins: [require('autoprefixer')],};
完成共用配置后,我們來添加開發(fā)環(huán)境下的配置。
// build/webpack.dev.conf.jsconst { merge } = require('webpack-merge');const webpackBaseConf = require('./wbepack.base.conf.js');const webpackDevConf = {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
hot: true,
hotOnly: true,
port: 8090,
host: '0.0.0.0',
overlay: true,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': '',
? ? ?},
? ?},
?},},};module.exports = merge(webpackBaseConf, webpackDevConf);
在開發(fā)環(huán)境下首先我們將mode
設(shè)置為development
,webpack
為不同模式下內(nèi)置了相應(yīng)的優(yōu)化。然后設(shè)置devtool
為eval-source-map
,devtool
用于控制是否生成以及如何生成source map
。eval-source-map
每個(gè)模塊使用eval()
執(zhí)行,并且將源地圖轉(zhuǎn)換為 DataUrl 后添加到eval()
中。初始化源地圖時(shí)比較慢,但是會(huì)在重新構(gòu)建時(shí)提供比較快的速度,并生成實(shí)際的文件。會(huì)映射到原始代碼中。它會(huì)生成用于開發(fā)環(huán)境的最佳品質(zhì)的源地圖。
source map 是一個(gè)信息文件,存儲(chǔ)著位置信息。用于轉(zhuǎn)換后代碼與轉(zhuǎn)換前代碼的位置的映射。
生產(chǎn)環(huán)境:
// build/webpack.prod.conf.jsconst {merge} = require('webpack-merge');const webpackBaseConf = require('./wbepack.base.conf.js');const { CleanWebpackPlugin } = require('clean-webpack-plugin');const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // 壓縮jsconst CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); // 壓縮cssconst webpackProdConf = {
mode: 'production',
plugins: [new CleanWebpackPlugin()],
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
// 在UglifyJs刪除沒有用到的代碼時(shí)不輸出警告 warnings: false,
compress: {
// 刪除所有的 `console` 語(yǔ)句,可以兼容ie瀏覽器 drop_console: true,
// 內(nèi)嵌定義了但是只用到一次的變量 collapse_vars: true,
// 提取出出現(xiàn)多次但是沒有定義成變量去引用的靜態(tài)值 reduce_vars: true,
? ? ? ? ? ? ? ? ?},
output: {
// 最緊湊的輸出 beautify: false,
// 刪除所有的注釋 comments: false,
? ? ? ? ? ? ? ? ?},
? ? ? ? ? ? ?},
? ? ? ? ?}),
new CssMinimizerPlugin(),
? ? ?],
?},}module.exports = merge{webpackBaseConf,webpackProdConf};
在生產(chǎn)環(huán)境下除了設(shè)置 mode,還添加了clean-webpack-plugin
用于在執(zhí)行打包命令前,清空 dist 文件中的文件。然后還添加optimization
選項(xiàng),該選項(xiàng)用于配置編譯代碼時(shí)一些優(yōu)化選項(xiàng)。optimization.minimizer
選項(xiàng)可以添加一個(gè)或多個(gè)壓縮工具,覆蓋默認(rèn)的壓縮工具。其中使用uglifyjs
壓縮代碼、去除注釋,console 等,通過這些選項(xiàng)來減少 js 代碼體積。css 代碼壓縮使用CssMinimizerPlugin
,該插件使用cssnano壓縮代碼。
通過環(huán)境我們做了 webpack 配置的區(qū)分,接下來我們需要去修改npm
命令,默認(rèn)執(zhí)行 webpack 命令時(shí),會(huì)解析項(xiàng)目根目錄的webpack.config.js
文件作為 webpack 的配置選項(xiàng),如果想指定 webpack 配置文件可以通過--config
{
"scripts": {
"serve": "cross-env NODE_ENV=development webpack serve --open --config build/webpack.dev.conf.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.dev.conf.js"}}
cross-env
用于設(shè)置 NODE_ENV 變量
以上就是在項(xiàng)目開發(fā)中常用的 webpack,更多可以查閱webpack 官網(wǎng)。

http://www.bluej.cn/newbluejsite/html/index.html
掃描下方二維碼
關(guān)注我們
期待每周與你相遇
暢游代碼世界

