webpack以一個默認(rèn)的入口文件作為打包的起點,將所有的資源依賴關(guān)系記錄,形成一個依賴關(guān)系樹狀圖,根據(jù)這個樹狀圖引入這些相關(guān)的資源。引入的資源形成一個chunk(代碼塊),分別將這些chunk進(jìn)行處理(例如將es6轉(zhuǎn)化,將sass預(yù)處理為css等,這些操作統(tǒng)一稱為打包),將打包好的文件輸出為靜態(tài)資源bundle
五個核心概念
-
Entry指示
webpack以哪個文件為打包的起點,并分析構(gòu)建好內(nèi)部資源依賴圖。 -
Output指示
webpack將打包后的資源bundle輸出到哪里,以及如何命名。 -
Loader讓
webpack能夠去處理那些非js代碼(webpack本身只理解js代碼),如同翻譯官的角色。loader模塊下載后,課配置直接使用。 -
Plugins可以用于執(zhí)行范圍更廣泛的任務(wù),包括從打包優(yōu)化和壓縮,到重新定義環(huán)境中的變量等。
plugins插件下載后,還需引入才可配置使用。 -
Modedevelopment:能讓代碼本地調(diào)試運行的環(huán)境production:能讓代碼優(yōu)化上線的運行環(huán)境
webpack能處理js/json資源,不能處理css/img等其他資源;打包的生產(chǎn)環(huán)境或開發(fā)環(huán)境代碼,ES6模塊化已經(jīng)編譯為瀏覽器能識別的模塊化;打包的生產(chǎn)環(huán)境比開發(fā)環(huán)境多一個js壓縮的過程。
webpack.config.js
指示webpack需要進(jìn)行的工作,運行webpack指令時,會加載文件內(nèi)的配置。所有構(gòu)建工具都是基于nodejs平臺運行的,默認(rèn)采用commonjs標(biāo)準(zhǔn)模塊化。
css文件編譯處理
style-loader:創(chuàng)建style標(biāo)簽,將已轉(zhuǎn)化為js的樣式資源插入,然后添加到head中生效;css-loader:將css文件轉(zhuǎn)化為commonjs模塊加載到j(luò)s中,模塊內(nèi)容為樣式字符串;less-loader:將less文件編譯成css文件,需要依賴less插件。
{
test: /\.css$/, //匹配文件
use: [ //use中l(wèi)oader執(zhí)行順序從后往前
'style-loader', //創(chuàng)建style標(biāo)簽,將js中的樣式資源插入,然后添加到head中生效
'css-loader', //將css文件變成commonjs模塊加載到j(luò)s中,模塊內(nèi)容為樣式字符串
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader', //將less文件編譯成css文件,需要依賴less插件
]
},
html文件編譯處理
html-webpack-plugin:不添加任何配置,默認(rèn)會創(chuàng)建一個空的html,自動引入打包輸出的所有資源(js/css);如果需要有結(jié)構(gòu)的html文件,可以進(jìn)行自定義配置。
new HtmlWebpackPlugin({
template: './src/index', //復(fù)制./src/index.html文件,并自動引入打包輸出的所有資源
})
圖片資源編譯處理
使用url-loader(依賴file-loader,默認(rèn)處理不了html中的img圖片,webpack5不再使用)處理圖片資源,limit配置表示圖片大小小于8kb的情況下,會通過base64處理轉(zhuǎn)換成字符串方式。base64可以減少請求數(shù)量,減輕服務(wù)器壓力,但同時圖片體積會更大,文件請求速度會更慢。
使用html-loader處理html文件的img圖片,該插件負(fù)責(zé)引入img圖片,從而能被url-loader進(jìn)行處理。由于url-loader是使用es6模塊處理文件,而html-loader使用commonJs引入圖片,解析會出問題:[object Module],此時url-loader應(yīng)使用esModule配置。
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[name]_[hash:10].[ext]', //name為圖片文件名;hash:10取圖片hash的前10位;ext取文件原來的擴(kuò)展名
esModule: false, //關(guān)閉url-loader的es6模塊化,使用commonJs解析
}
},
{
test: /\.html$/,
loader: 'html-loader'
}
在webpack5中我們使用assets-module,url-loader在這個版本中已經(jīng)被廢棄。
{
test: /\.(png|jpe?g|gif)/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024,
}
},
generator: {
filename: 'img/[name]_[hash:10].[ext]'
}
}
其他資源編譯處理
{
exclude: /\.(css|jpg|png|gif|html|less|js)$/,
loader: 'file-loader',
options: {
name: '[name]_[hash:10].[ext]'
}
}
devServer
開發(fā)服務(wù)器,用來做自動化,能夠自動編譯,自動打開瀏覽器,自動刷新瀏覽器等。只會在內(nèi)存中編譯打包,不會有任何輸出,output的配置將不再有用,終止服務(wù)器后,內(nèi)存中存在的編譯包將會自動刪除。啟動devServer指令為:webpack-dev-server(需下載該插件)
devServer: {
contentBase: resolve(__dirname, 'build'), //需要運行的項目的文件目錄(構(gòu)建后的目錄),webpack5不用配置該選項(該選項已被刪除)
compress: true, //啟動gzip壓縮,讓項目體積更小,啟動更快
port: 9006, //開發(fā)服務(wù)器的端口號
open: true, //自動打開瀏覽器
}
打包文件優(yōu)化
mini-css-extract-plugin插件可以將樣式文件從js代碼中抽離出來,單獨成一個文件目錄,通過link標(biāo)簽引入。將css單獨提取出來,可以減小js文件大小
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
{
test: /\.css$/, //匹配文件
use: [ //use中l(wèi)oader執(zhí)行順序從后往前
// 'style-loader',
MiniCssExtractPlugin.loader, //取代style-loader,提取出css代碼
'css-loader',
]
},
plugins: [
new MiniCssExtractPlugin({
filename: './css/built.css', //對輸出的css文件進(jìn)行重命名,注意該路徑為相對路徑
})
]
css兼容性處理
使用postcss,依賴于插件postcss-loader與postcss-preset-env(幫助postcss找到package.json中browserslist里面的配置,通過配置加載指定的css兼容性樣式,可以使兼容性精確到某一個瀏覽器版本。默認(rèn)是找生產(chǎn)環(huán)境browserslist,若要找development環(huán)境,需要設(shè)置node環(huán)境變量:process.env.NODE_ENV = development)
"browserslist": { //package.json
"development": [
"last 1 chrome version", //兼容至少最近的一個chrome瀏覽器版本
"last 1 safari version"
],
"production": [
">0.2%", //大于99.8%的瀏覽器
"not dead", //不要已經(jīng)廢棄的瀏覽器版本
"not op_mini all", //不要op_mini瀏覽器,webpack5已經(jīng)刪除
]
}
{ //webpack.config.js
test: /\.css$/, //匹配文件
use: [ //use中l(wèi)oader執(zhí)行順序從后往前
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss', //固定寫法
plugins: () => [
require('postcss-preset-env')()
]
}
},
{ //webpack5用法
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
},
}
]
},
css壓縮
使用插件optimize-css-assets-webpack-plugin,webpack.config.js中直接調(diào)用插件,默認(rèn)的配置已經(jīng)足夠。
js語法檢查
語法檢查只針對js文件,使用eslint-loader插件(依賴于eslint庫),應(yīng)該只檢查自己的源代碼,排除其他插件代碼。需要在package.json中eslintConfig設(shè)置具體的檢查規(guī)則。推薦使用airbnb風(fēng)格指南,指示如何規(guī)范的編寫js代碼。運用airbnb可以通過使用插件eslint-config-airbnb(包含react風(fēng)格建議)或eslint-config-airbnb-base(不包含react風(fēng)格建議)。
eslint-config-airbnb依賴于插件eslint,eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-jsx-a11y
eslint-config-airbnb-base依賴于插件eslint,eslint-plugin-import
{
test: /\.js$/,
exclude: /node_modules/,
enforce: 'pre', //優(yōu)先執(zhí)行
loader: 'eslint-loader',
options: {
fix: true, //自動修復(fù)代碼格式問題
}
}
//eslint-disable-next-line 不檢查下一行代碼
{ //packag.json
"eslintConfig": {
"extends": "airbnb-base" //使用該包進(jìn)行語法檢查
}
}
js兼容性處理
使用bable-loader插件,依賴于插件@babel/core與@babel/preset-env,只能轉(zhuǎn)換基本語法,如promise不能轉(zhuǎn)換。
{
test: /\.js/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], //指示babel做怎樣的兼容性處理
}
}
全部js兼容性處理還需插件@babel/polyfill,直接在js文件中引入import '@babel/polyfill'。該插件會將所有兼容性代碼全部引入,導(dǎo)致文件體積太大。
按需引入需要做兼容性處理的代碼,需要借助corejs,需要下載插件@babel/core。使用這種方法就不能同時使用第二種方法。
{ //有報錯
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage', //按需下載
corejs: {
version: 3, //指定corejs版本
},
targets: { //指定兼容性做到哪個版本瀏覽器
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17',
}
}
]
]
}
},
webpack性能優(yōu)化
開發(fā)環(huán)境性能優(yōu)化
-
優(yōu)化打包構(gòu)建速度
HMR:hot module replacement(熱模塊替換/模塊熱替換),一個模塊發(fā)生變化,只會重新打包這一個模塊(而不是打包整個模塊),不會重新刷新瀏覽器,極大提升構(gòu)建速度。devServer配置中添加hot: true就可以使用該功能。樣式文件由于style-loader內(nèi)部實現(xiàn)了HMR功能,可以實現(xiàn)熱更新。因此在開發(fā)環(huán)境中應(yīng)該使用style-loader,使樣式文件打包更快;html文件默認(rèn)不支持該功能,由于項目中只存在一個,html文件發(fā)生變化的時候也只需要更新這一個文件,因此不需要HMR功能;js文件默認(rèn)也不支持該功能,若要實現(xiàn)該功能(只能處理非入口js文件),則需要添加代碼:if(module.hot) { //為true則表示HMR功能為開啟狀態(tài) module.hot.accept('./print.js', () => { //module.hot.accept監(jiān)聽print.js文件,一旦該文件發(fā)生變化,其他模塊不會重新打包構(gòu)建,只會再次執(zhí)行當(dāng)前這個回調(diào)函數(shù) print(); }) } -
優(yōu)化代碼調(diào)試
source-map:一種提供源代碼到構(gòu)建后代碼映射的技術(shù),如果構(gòu)建后代碼出錯了,通過映射,可以追蹤到源代碼錯誤位置。使用方式:在webpack.config.js中添加配置devtool: 'source-map'cheap-module-source-map中module表示會將loader的source-map也加入。開發(fā)環(huán)境推薦eval-source-map更快,生產(chǎn)環(huán)境推薦source-map
生產(chǎn)環(huán)境性能
-
優(yōu)化打包構(gòu)建速度
one-of可以在webpack構(gòu)建的時候,相應(yīng)文件只用匹配一個規(guī)則,此是有一個問題,相同類型文件不能有兩個規(guī)則來處理。解決方法是,將處理相同文件的多個規(guī)則移出one-of。rules: [{}, {oneOf: []}]babel緩存:配置
babel-loader規(guī)則選項cacheDirectory為true,表示在第二次構(gòu)建時, 會讀取之前的緩存。使第二次構(gòu)建速度更快。 -
優(yōu)化代碼運行的性能
文件資源緩存:讓代碼上線運行緩存更好使用
hash:每次webpack構(gòu)建時,都會生成一個唯一的hash值,將js和css文件名加上hash值,可以使每次構(gòu)建后都能拿到最新的代碼。但由于webpack每次構(gòu)建都會生成一個新的hash值,導(dǎo)致所有的緩存都會失效。chunkhash:根據(jù)chunk生成的hash值,如果打包來源于同一個chunk,hash值就一樣。chunk:由入口文件加上入口文件中引入的js或css文件形成一個chunk。由于css是在js中被引入的,因此同屬于一個chunk,所以在其中部分模塊發(fā)生變化時,也會使這一個chunk相關(guān)的所有緩存失效。cotenthash:根據(jù)文件的內(nèi)容生成相應(yīng)的hash值,不同文件的hash值一定不一樣,從而實現(xiàn),當(dāng)前文件內(nèi)容更新,只更新當(dāng)前文件的hash值,獲取最新文件代碼,其他文件還是來自于緩存。tree shaking去除無用代碼,減少代碼體積。在使用ES6模塊 化,
webpack中mode為production時,會自動開啟該動能。如果配置sideEffects: false表示所有代碼都沒有副作用,都可以進(jìn)行tree shaking,這樣可能會導(dǎo)致直接引入的代碼,例如css引入被去除。配置sideEffects: [*.css],改類型文件將不會執(zhí)行tree shaking。代碼分割
可以自動將
node_modules中代碼單獨打包成一個chunk最終輸出;自動分析多入口chunk中,有沒有公共的文件,如果有,會打包成單獨的一個chunk;import動態(tài)導(dǎo)入語法,能將某個文件單獨打包,實現(xiàn)懶加載。{ optimization: { splitChunks: { chunks: 'all', } } }
多進(jìn)程打包
使用thread-loader模塊,開啟多進(jìn)程打包。在thread-loader配置后的loader將會開啟過進(jìn)程打包。但進(jìn)程啟動大約需要600ms,進(jìn)程通信也有一定的開銷,因此只有工作消耗時間比較長(js代碼比較多),才需要多進(jìn)程打包。
{
loader: 'thread-loader',
options: {
worker: 2
}
}