面試被問到webpack,可別只知道說 HtmlWebpackPlugin 了哇。
前期準備
安裝依賴
npm init -y
npm install webpack webpack-cli --save-dev
配置打包命令
// package.json
{"scripts": {// ... 其他配置信息"build": "webpack --mode production"}
}
基本配置
entry
通過entry指定項目打包時的入口文件,可以配置單入口或者多入口
- 單入口文件
module.exports = {entry: './path/to/my/entry/file.js',
};
- 多入口文件
module.exports = {entry: {index: './src/index.js',info: './src/information.js',},
};
output
通過output配置項目打包后的文件名以及文件的輸出路徑(位置)
- filename:此選項決定了每個輸出 bundle 的名稱
-
什么是bundle?
根據入口文件,最后打包生成的、可以直接讓瀏覽器解析的JS、CSS文件。
-
JS屬于入口文件,那CSS為什么也可看作bundle?
- 不對CSS做抽離,那CSS會被內聯在JS中(必然算);
- 對CSS做抽離,但CSS也是通過JS去做處理的(算做樣式bundle);
-
html算不算?
HTML文件從嚴格意義上講不算做bundle,因為不包含直接打包的代碼或資源;
只是對打包好的資源做加載或展示;
-
CODE
module.exports = {output: {// 為什么這樣命名?方便應用瀏覽器的緩存機制filename: 'js/[name].[hash:8].js',} }
-
????????webpack官方:output->filename?
????????webpack官方:緩存(輸出哈希文件名)
- path:打包時會將 bundle 寫入到output.path選項指定的目錄下
-
官方給出:對應一個絕對路徑,如何配置它的值?
絕對路徑不做解釋,懂得都懂,不懂得自查;
在node下提供了一個全局變量
__dirname
,表示的就是當前執行的腳本文件所在的絕對路徑; -
CODE
module.exports = {output: {filename: 'js/[name].[hash:8].js',// 將打包后的資源輸出到當前目錄下的 dist 目錄中path: path.resolve(__dirname, 'dist')} }
-
- assetModuleFilename:與output.filename相同,不過應用于Asset Modules(字體or圖標)
- 支持的占位符:
[name]
,[file]
,[query]
,[ext]
,[hash]
與[path]
- name:原始文件名
- file:完整文件名(帶拓展名)
- query:URL查詢參數部分——圖的大中小到底請求哪個?(x.png?size=large),得到的是問號及后面的部分;
- ext:文件拓展名
- hash:根據文件內容生成的哈希值|區別與chunkhash和contenthash,是整個項目構建過程的哈希值,現在V5中叫fullhash。
- path:文件的相對路徑。
- 同內容但更新了文件名?涉及到文件名及內容,避免延遲更新。
- 注意:
- 該配置項是全局的,全局優先級低于局部的;
- 局部指的是后面通過asset系列方式對資源文件做解析時配置的generato.filename。
- CODE
module.exports = {output: {filename: 'js/[name].[hash:8].js',path: path.resolve(__dirname, 'dist'),// 將資源文件輸出到 assets 目錄下以hash碼來命名assetModuleFilename: 'assets/[hash][ext]',} }
- 支持的占位符:
- clean:控制是否在生成文件之前清空打包輸出目錄
- 布爾值true(清空)、false(不清空)
- 或以對象形式配置,使用keep來設置保留某個目錄下的文件
- CODE
module.exports = {output: {filename: 'js/[name].[hash:8].js',path: path.resolve(__dirname, 'dist'),assetModuleFilename: 'assets/[hash][ext]',clean:true} }
optimization
優化:比如對代碼進行壓縮、分割代碼、去除未使用的導出模塊、去除空chunk等
- minimize:告知 webpack 使用TerserPlugin或其它在optimization.minimizer定義的插件壓縮 bundle;值為布爾類型。
- CODE
module.exports = {//...optimization: {minimize: true,}, };
- minimizer:允許開發者通過提供一個或多個定制過的TerserPlugin實例,覆蓋默認的代碼壓縮工具,通過去除文件的換行、空格、注釋等字符來縮小文件體積。
- teser-webpack-plugin
- 該插件使用terser來壓縮Javascript(terser也是一個第三方依賴)
- 非內置,非webpack官方插件,需要安裝使用
- webpack官方:terser-webpack-plugin
- CODE
npm install terser-webpack-plugin --save-dev
const TerserPlugin = require('terser-webpack-plugin');module.exports = {optimization: {minimize: true,// 為啥是數組?你就用一個嗎?接受的類型就是數組類型minimizer: [new TerserPlugin()],}, };
- css-minimizer-webpack-plugin
- 該插件使用cssnano來壓縮CSS(cssnano是一個第三方庫)
- webpack貢獻,不過也需要安裝使用
- 注意:webpack只能識別處理Js文件,對CSS的壓縮處理記得配置好它的加載器(下面說這個東西)。
- webpack官方:css-minimizer-webpack-plugin
- ?CODE
npm install css-minimizer-webpack-plugin --save-dev ## 安裝加載器 npm install mini-css-extract-plugin --save-dev
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');module.exports = {// 當前的主角配置optimization: {minimize: true,minimizer: [new CssMinimizerPlugin(),],},module: {rules: [{test: /.s?css$/,use: [MiniCssExtractPlugin.loader, 'css-loader'],},],},plugins: [new MiniCssExtractPlugin()], };
- teser-webpack-plugin
加載器Loader
加載器在webpack這里有一個統稱,叫loader,用于輔助webpack來加載非JS類型的文件,需要注意使用順序。
webpack默認情況下只能處理Js文件,如果要處理其他文件,前提得讓webpack能夠加載其他文件,比如圖片、CSS文件等。
配置在module
>rules
中,rules
是一個對象數組,對于某類文件的解析及配置寫在對象中,其中test
指定某類文件,use
指定所需的加載器們。
style-loader
- style-loader:將 CSS 代碼以
<style>
標簽的形式插入到 HTML 文檔的<head>
部分。- 當 Webpack 處理 CSS 文件時,通常和
css-loader
一起使用,css-loader
會把 CSS 文件轉換為 JavaScript 模塊,style-loader
則會提取這個模塊中的 CSS 代碼,并動態地創建<style>
標簽將其插入到 HTML 頁面中。 - CODE
module.exports = {module: {rules: [{test: /\.css$/i,use: ['style-loader'],},],}, };
- 當 Webpack 處理 CSS 文件時,通常和
css-loader
- css-loader:解析CSS文件,
css-loader
會對@import
和url()
進行處理,就像 js 解析import/require()
一樣- 啟用/禁用
url
/image-set
函數進行處理。 如果設置為false
,css-loader
將不會解析url
或者image-set
中的任何路徑。 - webpack官方:css-loader
- CODE
module.exports = {module: {rules: [{test: /\.css$/i,use: ['style-loader',{loader:"css-loader",options:{url: true // 解析 CSS 中的 url()——處理背景圖時需要}}],},],}, };
- 啟用/禁用
MiniCssExtractPlugin.loader
- MiniCssExtractPlugin.loader:會將 CSS 代碼提取出來,然后由
MiniCssExtractPlugin
插件將這些 CSS 代碼合并到一個或多個獨立的 CSS 文件中,最后在 HTML 文件中通過<link>
標簽引入這些獨立的 CSS 文件。- 區別于style-loader:
- 將CSS抽離成單獨的文件,而不是以style形式插入html;
- 將抽離出來的css文件以link方式引入到html中。
- CODE
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');module.exports = {optimization: {minimize: true,minimizer: [new CssMinimizerPlugin(),],},module: {rules: [// 當前的主角配置{test: /.s?css$/,use: [MiniCssExtractPlugin.loader, 'css-loader'],},],},// 對應插件plugins: [new MiniCssExtractPlugin()], };
- 區別于style-loader:
postcss-loader
- postcss-loader:讓 Webpack 能夠使用 PostCSS 對 CSS 文件進行處理。
- 注意:
- postcss-loader和postcss不一樣,一個是loader一個是插件工具(下面講)。
- 使用postcss-loader還要下載postcss,要么鋪的路沒人走。
- webpack官方:postcss-loader
npm install --save-dev postcss-loader postcss
- 注意:
html-loader
- html-loader:解析HTML文件
- 將HTML導出為字符串,還可以對html字符串進行壓縮處理
- CODE
module.exports = {module: {rules: [{test: /\.html$/i,loader: 'html-loader',options: {// 開啟壓縮minimize: true,// 處理 HTML 中的資源引用sources: true, },},],}, };
html-withimg-loader
- html-withimg-loader:基于html-loader進行拓展,解決一些問題。
- 可以對圖片資源的輸出進行配置,base64或是圖片文件;
- 可處理通過include引入的子頁面,子頁面中的圖片資源也會處理。
asset module
資源模塊(asset module)是一種模塊類型,它允許使用資源文件(字體,圖標等)而無需配置額外 loader。
在 webpack 5 之前,通常使用:
- file-loader將文件發送到輸出目錄
- url-loader將文件作為 data URI 內聯到 bundle 中
- raw-loader將文件導入為字符串
資源模塊類型(asset module type),通過添加 4 種新的模塊類型,來替換所有這些 loader:
asset/resource
- 將資源文件發送到輸出目錄,并導出 URL(在代碼中使用該資源時就會使用這個URL路徑)。
- 之前通過使用
file-loader
實現。 - 對于較大的文件:圖片或字體文件等,需要單獨打包到輸出目錄,方便瀏覽器緩存管理。
- CODE
module.exports = {// ...其他配置~module: {rules: [{test: /\.(png|jpeg|jpg|gif)$/i,// 關鍵配置:僅匹配背景圖路徑(示例:src/css/imgs/ 目錄)include: path.resolve(__dirname, 'src/css/imgs'),type: 'asset/resource',generator: {filename: 'bg-images/[name].[contenthash][ext]'}},{test: /\.(png|jpeg|jpg|gif)$/i,// 關鍵配置:排除背景圖路徑,匹配其他圖片(示例:src/assets/images/ 目錄)exclude: path.resolve(__dirname, 'src/css/imgs'),type: 'asset/resource',generator: {// 為什么這里沒有用哈希編碼命名?因為資源名稱涉及網絡動態設定。filename: 'images/[name][ext]'}}]} };
asset/inline
- 導出一個資源的 data URI,也就是會將資源轉換為Base64編碼的數據。
- 之前通過使用
url-loader
實現。 - 將文件內容直接做編碼化處理,避免額外的http請求。
asset/source
- 導出資源的源代碼,也就是會將資源的內容以字符串的形式引入到JS模塊中。
- 之前通過使用
raw-loader
實現。 - 譬如針對txt、md等文件。
asset
- 在導出一個 data URI 和發送一個單獨的文件之間自動選擇。之前通過使用
url-loader
,并且配置資源體積限制實現。 - 默認情況下:小于8KB會使用
asset/inline
,否則使用asset/resource
;可以使用parser.dataUrlCondition
來更改臨界值。 - 資源模塊 | webpack 中文文檔
- CODE
module.exports = {// ...其他配置module: {rules: [{test: /\.(png|jpg|jpeg|gif)$/i,type: 'asset',parser: {dataUrlCondition: {maxSize: 4 * 1024 // 調整閾值為 4KB}},generator: {filename: 'images/[name].[contenthash][ext]'}}]} };
- 在導出一個 data URI 和發送一個單獨的文件之間自動選擇。之前通過使用
其他
-
sass-loader:輔助webpack來加載sass文件。
sass-loader | webpack 中文文檔
-
less-loader:輔助webpack來加載less文件。
less-loader | webpack 中文文檔
插件
html-webpack-plugin
html-webpack-plugin:根據html模板文件來將打包后的資源自動注入進該模版文件中,最后生成HTML文件。
- 自動化注入資源:在 Webpack 打包過程中,生成的 JavaScript 和 CSS 文件的文件名可能會包含哈希值,手動在 HTML 文件中更新這些引用會很繁瑣。
html-webpack-plugin
可以自動完成這個任務,確保 HTML 文件引用的是最新的打包資源,以script或link引入相關資源。 - 支持多頁面應用:可以通過多次實例化該插件,為多頁面應用的每個頁面生成對應的 HTML 文件,并且為每個頁面注入相應的資源。
- 自定義模板:允許使用自定義的 HTML 模板,開發者可以在模板中使用 EJS 語法嵌入變量和邏輯,靈活控制生成的 HTML 內容。
- chunks指定:多頁面應用會有多個入口文件,每個頁面可能需要不同的代碼塊,chunks可以為每個頁面精確指定要引入哪些代碼塊。為什么一個html有引入多個css,但最后webpack知道在哪個html中關聯哪幾個css資源,哪些css屬于html指定的chunks,那這些css最后就會link進html。
- HtmlWebpackPlugin | webpack 中文文檔
- CODE
npm i html-webpack-plugin --save-dev
const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {entry: {index: './src/js/index.js',detail: './src/js/info.js',},// ...其他配置plugins: [new HtmlWebpackPlugin({template: './public/index.html',filename: 'index.html',chunks: ['index'] // 只注入 index 代碼塊,也是因為這里的設置才讓對應的css能夠引入到html中,因為css屬于chunk,chunk關聯html}),new HtmlWebpackPlugin({template: './public/detail.html',filename: 'detail.html',chunks: ['detail'] // 只注入 detail 代碼塊})]
};
mini-css-extract-plugin
MiniCssExtractPlugin:為每個包含 CSS 的 JS 文件創建一個 CSS 文件,并將CSS代碼抽離到單獨的文件中,而不會讓CSS嵌入在Js中。
- 官方建議同css-loader一起使用,哈哈要不然怎么處理css呢,凈是沒用的大實話
- CODE
const MiniCssExtractPlugin = require("mini-css-extract-plugin");module.exports = {plugins: [new MiniCssExtractPlugin({filename: 'css/[name].[hash:8].css'})],module: {rules: [{test: /\.css$/i,use: [MiniCssExtractPlugin.loader, "css-loader"],},],},
};
css-minimizer-webpack-plugin
CssMinimizerPlugin:對每個CSS文件進行壓縮處理。
- 默認情況下只在生產環境下開啟CSS壓縮優化,如果想要在開發環境下也啟用CSS優化,需要將
optimization.minimize
設置為true
。 - CODE
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');module.exports = {module: {rules: [{test: /.s?css$/,use: [MiniCssExtractPlugin.loader, 'css-loader'],},],},optimization: {minimizer: [new CssMinimizerPlugin(),],},plugins: [new MiniCssExtractPlugin()],
};
postcss
postcss :一個CSS處理引擎,需要結合postcss-loader來使用在webpack中,有autoprefixer、cssnano、postcss-preset-env等常用插件。
- autoprefixer
-
為解決瀏覽器兼容問題,為css相關樣式添加前綴。
-
需要安裝postcss-loader、postcss、autoprefixer
Autoprefixer CSS online
-
CODE
npm i postcss-loader postcss autoprefixer -D
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = {module: {rules: [{test: /\.css$/i,use: [MiniCssExtractPlugin.loader,'css-loader','postcss-loader'],},],}, };
-
- 注意:
- 添加前綴也是根據指定的瀏覽器列表來添加的,而瀏覽器列表正是package.json中的browserslist;
- 創建`postcss.config.js`文件,內部引入autoprefixer,或在webpack中直接設置對應的配置項;
// package.json {// 其他配置"browserslist": ["last 2 versions", "ie >= 8", "Chrome > 31", "> 1%"] }
// postcss.config.js module.exports = {plugins: [require('autoprefixer')] }
terser-webpack-plugin
TerserWebpackPlugin:雖然由社區成員維護的第三方包,基于terser來對Javascript進行壓縮處理,在webpack中開箱即用,安裝配置即可。
- CODE
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {optimization: {minimize: true,minimizer: [new TerserPlugin()]}
}
補充問題
批量導入圖片
// 自動導入 ../images 目錄下所有擴展名為 .png、.jpg、.jpeg 或 .gif 的圖片文件
require.context('../images', false, /\.(png|jpe?g|gif)$/)
// 由于這些圖片可能在多個html中都會用到,所以可以創建一個入口文件寫入該代碼,配置在entry中,這樣webpack就可以知道對這些圖片做模塊打包了。
'../images'
:這是第一個參數,表示要搜索的目錄路徑。在這個例子中,Webpack 會從當前文件所在目錄的上一級的images
目錄開始搜索符合條件的文件。false
:第二個參數是一個布爾值,用于指定是否要遞歸搜索子目錄。false
表示不進行遞歸搜索,即只在../images
目錄下查找文件,而不會深入到其子目錄中。如果設置為true
,則會遞歸搜索該目錄下的所有子目錄。/.(png|jpe?g|gif)$/
:第三個參數是一個正則表達式,用于篩選符合條件的文件。這個正則表達式表示只匹配擴展名為.png
、.jpg
、.jpeg
或.gif
的文件。
樣式正常,報錯找不到CSS
由于使用了html-webpack-plugin,可以自動對css和js文件通過link或script插入到html模板中,所以檢查是否模板并非模版而是原始的html文件;或者html模板中有引入相對路徑下的源碼文件。
<!-- 刪除以下行(插件會自動注入) -->
<!-- <link rel="stylesheet" href="./css/index.css"> -->
<!-- 刪除手動引入的 CSS 鏈接,HtmlWebpackPlugin 會自動注入正確的路徑 -->
哈希緩存
文件哈希+瀏覽器緩存
譬如資源的304狀態碼,其實是由于瀏覽器中緩存了該資源,且本次請求的該資源同緩存的資源一致,沒有變化,直接使用緩存的資源。
響應頭中的ETag
就是處理文件讀取的關鍵點,是服務器響應資源時返回的一個唯一標識符,用于標識資源的具體版本。它通常基于資源內容生成(如哈希值、文件大小、修改時間等),類似于文件的 “指紋”。
- 瀏覽器再次請求同一資源時,會先檢查本地緩存是否存在。若存在,會向服務器發送條件請求,通過If-None-Match頭攜帶緩存中的
Etag
,詢問服務器資源是否更新:-
若資源未更新:
服務器返回
304 Not Modified
,告知瀏覽器可直接使用本地緩存,不返回資源內容,減少帶寬消耗。 -
若資源已更新:
服務器返回新資源內容和新的
Etag
,瀏覽器更新緩存。
-
與 Cache-Control/Expires 的配合
Cache-Control
:控制緩存的 “有效期”(如max-age=31536000
表示緩存 1 年)。Etag
:在緩存過期后,作為精確驗證手段,確保瀏覽器獲取到最新資源。- 若
Cache-Control
未過期,瀏覽器直接使用緩存,不觸發 Etag 驗證; - 若
Cache-Control
過期,瀏覽器發送帶If-None-Match
的請求,通過 Etag 驗證資源是否真的需要更新。
- 若
- 若同時使用
Last-Modified
和Etag
,服務器會優先驗證Etag
(If-None-Match
優先級高于If-Modified-Since
)。