前言
通過上篇文章——弄懂這幾個概念後,我對webpack有了更新的理解 的講解,我們大致瞭解了:
- 使用
webpack過程中出現的一些概念 - 結合打包過程中出現的概念,淺析了
webpack打包流程,讓大家對打包流程有個大致的理解
我們都知道,我們在實際開發過程中,使用webpack無非就是為了最後輸出瀏覽器能運行的css、img、js、html等前端資源。
那麼,為了更貼近實際,我們這篇文章,就以如何輸出css、img、js、html等前端資源為目的,學習一下如何配置webpack。
學習大綱
這篇文章主要講解以下基礎配置:
- 處理
css、less文件 - 處理圖片(
img)、字體(font)、音樂(audio)等前端資源文件 - 編譯
es6+語法及API - 處理
html文件
webpack文檔對配置寫的算挺詳細了,所以這裏我也不想複製粘貼又寫一遍,這樣不如大家直接看文檔。
授人以魚不如授人以漁,學習不能硬學,應該要有一些方法跟技巧。因此,我更想在講解配置的過程中,總結一些方法,讓我們更好的理解webpack的配置項。這樣,一些類似的一些配置,我們照葫蘆畫瓢,看文檔就可以配出來。
備註
文章涉及到的案例已經上傳到 github:
- 為了閲讀方便,文章只貼了相關代碼,建議
fork一下,看看完整代碼;或者跟着文章一起邊看邊敲,這樣印象會更深刻一些 - 創作不易,如果覺得有幫助的話,歡迎
star🌟
處理css、less文件
我們先用 learn-03 這個案例來看看webpack如何處理樣式文件。
我們安裝相應的loader解析:
less-loader:解析less為csscss-loader:將css解析為webpack能識別的有效模塊style-loader:將解析出來的css插入到<header />中
npm install less less-loader css-loader style-loader -D
然後配置一下:
// webpack.config.js
module.exports = {
...,
module: {
rules: [
{
test: /.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
]
}
}
loader的解析是逆序執行(或從右到左),因此這個配置會按照(less-loader → css-loader → style-loader)以下順序執行:
- 將
less解析成css - 再將結果傳給
css-loader,解析成webpack認識的有效模塊 - 再將結果傳給
style-loader,將解析出來的樣式插入到<header />)的順序執行。
less、css、入口文件代碼為:
// index.less
*{
margin: 0;
padding: 0;
}
h1{
color: red;
margin-top: 20px;
}
/* index.css */
h2{
color: green;
}
// index.js
import './assets/index.less';
import './assets/index.css';
輸出:
我們會發現我們寫的樣式處理成功,並且被插入到<head/>中。
總結
我們 上篇文章 説過:
- 我們通過
import或require進來的資源文件,或我們項目中的每個文件,都可以看作為一個獨立的模塊 Loader的作用就是把這些模塊(module)轉換成webpack能夠識別的有效模塊
webpack人性化的暴露了module配置項,就是專門用來配置相關loader,以此解析相應的module。因此,如果我們想解析我們源碼中使用的某些模塊,我們就應該:
- 先下載相應的
loader - 直接找到
module配置項,根據loader使用方法直接配置
所以,我們按照上面總結的方法跟思想,可以試試自己配置處理一下.scss或者.txt文件
ok,我們成功得到了我們項目需要的樣式(css),接下來我們再看看如何處理圖片(img)等前端資源文件
處理前端資源文件
前端資源文件很多,在這裏,我們粗略的把它們先分成兩類:
- 常用類:一般就是
img、font、video等文件 - 特殊類:
.csv、.xml等文件
這些資源只要import或者require進我們的源碼,那麼它一樣也是模塊。既然是模塊,我們就需要安裝相應的loader進行解析,然後在module配置項配置。
在wepack5之前,對於常用類的資源,我們一般需要安裝以下loader來解析:
raw-loader:將文件解析成字符串file-loader:將import/require()進來的文件,解析為一個完整引用的url,並將文件發送到輸出目錄中url-loader:可以把文件轉成base64
我們經常用到的是file-loader、url-loader這兩個。因為常用類資源,經常以url形式引入到我們項目中;或者為了減少http請求,直接轉換成base64
但是在webpack5後,webpack已經內置上述幾個loader的功能,所以我們可以不用再安裝以上的loader了;我們可以直接使用webpack自帶的 asset module。
webpack5之後內置的資源模塊:
asset/source:等同於raw-loaderasset/resource: 等同於file-loaderasset/inline:等同於url-loaderasset:比較靈活,同時擁有asset/resource與asset/inline的功能。如果設置了文件大小限制,文件沒有超出這個大小的話,會轉為base64;如果沒設置大小限制,則功能跟file-loader一樣
這是我十分愛webpack5的原因,因為我們不要安裝那麼多亂七八糟的loader
我們用 learn-04 這個案例,看看怎麼使用資源模塊來解析我們的前端資源。
我們把上一章解析樣式的配置一起加進來,在less跟js中分別使用圖片資源。
我們有兩個圖片:
preview.gif:大小為349kbecj-cli.png:大小為4kb
// webpack.config.js
module.exports = {
...,
module: {
rules: [
{
test: /.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 1024 * 10 // 當圖片 < 10kb 轉成base64
}
},
generator: {
filename: '[name][ext]' // 設置輸出的文件名字
}
}
]
}
}
入口文件:
// index.js
import './assets/index.less';
const img = new Image();
img.src = require('./assets/ecj-cli.png');
img.width = 500;
document.querySelector('#js_logo').append(img);
樣式:
.bg_logo{
width: (1434/3px);
height: (1212/3px);
background-image: url('./preview.gif');
background-size: 100%;
}
輸出結果:
我們會發現樣式跟js都處理圖片成功:
preview.gif大於10kb,所以沒有被轉為base64,而是返回了一個完整的引用路徑(相當於file-loader的功能)ejc-cli.png小於10kb的則被轉為了base64(相當於url-loader的功能)。
上面的gif圖,是我開發的一個在日常項目中十分實用的命令行工具 ejc-cli 。它可以將對接人員收集的
Excel數據,按一定格式轉成我們代碼所需要的json數據。它十分方便我們開發維護數據,並且還有跟其他人員對接數據。感興趣的朋友可以看看 👉🏻 瞭解 ejc-cli
總結
通過import或者require進我們源碼的資源,一樣是一種模塊。所以需要下載相應的loader,然後到module配置項進行配置。
對於常用類資源(img、font、video、audio等等):
- 因為常用,所以
webpack5把處理它們的功能內置了,這樣我們不用額外安裝loader,我們直接使用asset module來管理 - 我們一般都使用
asset/resource、asset/inline、asset這幾個模塊來管理常用類資源。因為我們常用類的資源,經常會在我們項目中引入使用,我們需要把它們解析成引入完整的url,或者為了減少http請求,直接轉換成base64
對於特殊類資源(.csv、.xml等等):
- 我們可以理解為,因為不常用,所以
webpack沒有把它們的功能內置 - 因此,我們需要安裝相應的
loader來解析。例如我們想解析.csv文件,我們需要安裝csv-loader來解析
按照上面總結的方法,可以試試自己配置處理一下video或者font等文件
好,到目前,我們已經得到了css、img。我們接下來看看,如何通過webpack處理得到我們的js
編譯es6+
時至今日,js發展迅速,為了提高開發效率和代碼質量,每年都會有一些新的語法或者新的API出現。
例如,在語法方面出現了:
- 箭頭函數:
()=> - 類語法糖:
class People {} - 解構賦值:
const [x, y] = [1, 2]; - ...
在API方面又有:
PromiseArray.prototype.includes()Object.keys()- ...
但是這些新的內容在一些低版本瀏覽器上是不支持的,所以我們需要通過webpack來將我們的es6+編譯成這些低版本瀏覽器能支持的版本。
我們先用 learn-05 這個案例,看看我們怎麼配置webpack編譯es6+。
我們安裝相關依賴:
...
"devDependencies": {
"@babel/core": "^7.22.8",
"@babel/plugin-transform-runtime": "^7.22.7",
"@babel/preset-env": "^7.22.7",
"babel-loader": "^9.1.3",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"core-js": "^3.31.1"
},
入口文件。使用了class、Promise、()=>{}等es6+語法及API:
// index.js
class People {
constructor(name) {
this.name = name;
}
sayHi() {
return Promise.resolve(this.name);
}
}
const Lee = new People('Lee');
Lee.sayHi().then(name => console.log(`Hi, I am ${name}.`));
配置webpack:
// webpack.config.js
module.exports = {
...,
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
}
}
配置Babel。在項目根目錄新建babel.config.js文件:
// babel.config.js
const presets = [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: require('core-js/package.json').version,
proposals: true
}
}
]
];
const plugins = [
'@babel/plugin-transform-runtime',
];
module.exports = {presets, plugins}
設置要兼容的目標瀏覽器。在package.json添加browserslist字段:
"browserslist": [
"ie 11"
]
在ie 11運行的結果:
我們會發現:
- 我們寫的
es6+在ie 11運行成功 Promise這個es6+API,也被添加到了ie 11裏
説明我們編譯的es6+是成功的。
關於Babel
在上面配置中,大家會發現,如果我們想用webpack編譯es6+,我們還需要在根目錄添加babel.config.js文件,步驟比處理其他模塊要複雜麻煩的多。這是因為,編譯es6+的核心,是需要Babel來配合的。Babel是編譯es6+的一個工具。
由於本篇文章主要是講解webpack,所以在這裏,我們對Babel就大概提一下。以下是Babel常用的一些配置。
-
如果我們開發的項目是應用程序或大型的項目,我們可以這麼配置:
const presets = [ [ '@babel/preset-env', { modules: false, useBuiltIns: 'entry', // or useBuiltIns: 'usage', corejs: { version: '3.27.2', proposals: true } } ] ]; const plugins = [ '@babel/plugin-transform-runtime' ]; module.exports = {plugins, presets}; // index.js // If use useBuiltIns: 'entry' import 'core-js/stable' -
如果我們是想開發一個第三方庫,我們可以這麼配置:
// Babel配置 const presets = [ [ '@babel/preset-env', { modules: false } ] ]; const plugins = [ [ '@babel/plugin-transform-runtime', { corejs: { version: 3, proposals: true } } ] ]; module.exports = {plugins, presets};
如果你想對Babel已經有個瞭解,但處於懵懵懂懂的狀態;又或者你想學習Babel,可以看看我之前寫的關於Babel的專欄 👉🏻 Babel專欄。
相信我,看完你一定會有所收穫。
總結
js一樣是import或者require進我們的源碼,那麼它也是模塊。所以我們也要下載對應的loader(babel-loader)並且在module配置項配置它。- 此外,我們除了配置
babel-loader,最重要的是還需要配置Babel(babel.config.js)。
所以,如果想要“編譯es6+”,我們除了要配置webapck,最重要的是我們還要配置babel.config.js。如果你不想只是一味的複製粘貼Babel配置,你還必須得學習Babel相關知識。
還記得我們使用webpack的目的嗎?輸出瀏覽器能運行的css、img、js、html等前端資源。
OK,到目前我們已經通過webpack處理了css、img、js。我們再來看看如何通過webpack處理得到我們的html
處理html文件
在上面的例子中,我們想在瀏覽器看效果,我們需要:
- 在根目錄新建一個
html文件 - 在新建的
html文件中,手動引入打包出來的資源文件
這個過程太麻煩了,有沒有一種辦法,我們只用提供一個html模板文件,然後通過webpack編譯後,它自動幫我們把打包後的文件都引入好。這樣,我們打包完直接運行html就能在瀏覽器看效果就好了呢?
webpack這麼人性化的東西,當然是有的。
我們先用 learn-06 這裏例子看看如何利用webpack達到我們上述的效果。
安裝相關插件:
npm i html-webpack-plugin -D
這次webpack配置來點不一樣的:
我們設置打包後的js儲存目錄為js文件夾;為打包後的js名字增加5個長度的hash值。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: './js/[name]-[chunkhash:5].js',
clean: true
},
plugins: [
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, './dist/[name].html'),
template: path.resolve(__dirname, './index.html'),
title: 'HtmlWebpackPlugin',
})
],
}
打包後的文件結構為:
dist
├── index.html
└── js
└── index-efbfd.js
打包後的html如下:
我們會發現:
- 我們的
html、js都被打包輸出到dist文件夾下,這十分方便我們部署(之前我們是在根目錄下新建index.html,因此打包後dist文件夾內只有img、js等文件) - 打包後的
html會幫我們自動引入好我們打包後的js文件
分析
從上面的例子我們可以看出,想要webpack處理html文件,我們是需要在webpack提供的plugins配置項來配置的。可能有些人會疑惑html也是一個模塊,為什麼是在plugins配置,而不是在module裏面配置?我們來分析一下。
我們項目所有文件當然可以看作是一個模塊,但是在module裏面配置還是在plugins裏面配置,取決於我們對這些模塊的使用目的。一般我們在自己的源碼中使用到這個模塊,則需要相應的Loader來解析。
在上文講解如何處理img、js時,我們的目的是為了在源碼中解析這些文件:
img,我們需要將它轉化成base64js,我們需要將es6+的東西編譯成es5及以下
並且,我們的文件都是通過import或是require()的方式,引入到我們的項目中,因此這時候當然需要相應的Loader來轉換這些模塊。
但是在處理html文件時,我們的目的並不是為了要解析html,我們只是想讓打包出來的html自動引用我們的js、img,這更像是使用自動引入的功能;而且,我們也沒有把我們的html引入進我們的項目,因此我們當然不需要相應的Loader來解析。
還記得我們 上篇文章 我們講解過:Loader用於轉化模塊,Plugin則用來加強webpack打包編譯時的功能。
在此處我們處理html文件,目的更多側重的是想要讓打包出來的html自動引用我們的js、img這個功能。因此我們需要在plugins配置項來配置,增強webpack打包編譯時的功能。
同理,假設我們想要在項目中解析某個html文件(在我們項目中,import html from './x.html'),那我們就得安裝相應的loader(html-loader),並且在module配置項目中配置。
總結
在module還是plugins配置項裏配置,取決於我們對這些模塊的使用目的。
至此,我們已經成功通過配置webpack拿到了一個項目中需要的css、img、js、html;這也相當於學會了webpack的一些基本配置。
大家可以通過上面總結的一些方法,自己完成對sass、font、video等資源的處理,加深一下印象。
學習總結
通過上面對一些配置項的講解,我們可以有以下總結:
- 想要解析
css,需要安裝相應的loader,並且在module裏配置 - 想要解析
img、font、video等常用類資源,我們不用安裝loader(webpack已經內置解析他們的功能)。我們一般在module配置項裏,使用asset/resource、asset/inline、asset這三個內置模塊來解析 - 想要解析
js,我們除了要安裝相應的loader(babel-loader),在module配置項裏配置,最重要的是我們還要學習Babel的相關知識(感興趣可以看看我的 👉 Babel專欄) - 想要解析
html文件,我們需要在plugins配置項裏配置html-webpack-plugin插件 - 在
module還是plugins配置項裏配置,取決於我們對這些模塊的使用目的
通過上面的學習,我們整理一下,輸出一個完整的webpack基礎配置,讓大家加深印象:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: './js/[name]-[chunkhash:5].js',
clean: true
},
module: {
rules: [
{
test: /.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 1024 * 10 // When the image size is < 10kb it will be converted to base64
}
},
generator: {
filename: '[name]-[hash:5][ext]' // Set the name of the output file
}
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, './dist/[name].html'),
template: path.resolve(__dirname, './src/index.html'),
title: 'Webpack Basic Configuration',
})
],
}
完整的基礎配置在 learn-07 ,建議大家看看完整版,這裏只貼了webpack.config.js
最後
- 希望這篇文章的一些方法總結,學習目的能讓大家更好的學會如何配置
webapck - 後面的文章將會深入配置。現實項目中,大多分開發環境、生產環境,因此我們將學習如何針對開發環境、生產環境不同的環境,進行不同
webpack的配置,這更貼近我們現實項目。如果感興趣的話可以關注一下這個 👉 專欄 - 文章涉及到的
Babel知識,如果你感興趣,想學習,也可以看看 👉 Babel專欄 - 文章涉及到的案例已經上傳到 github,歡迎
star或fork學習
最後的最後,如果大家覺得文章有幫助到,創作不易,還請大家多點贊收藏評論,如果有異同點,歡迎評論討論。