
什么是插件(plugin)?
plugin 用于擴(kuò)展webpack的功能。
當(dāng)然loader也是變相的擴(kuò)展了webpack ,但是它只專注于轉(zhuǎn)化文件這一個(gè)領(lǐng)域。而plugin的功能更加的豐富,而不僅局限于資源的加載。在 webpack 的構(gòu)建流程中,plugin 用于處理更多其他的一些構(gòu)建任務(wù)??梢赃@么理解,模塊代碼轉(zhuǎn)換的工作由 loader 來(lái)處理,除此之外的其他任何工作都可以交由 plugin 來(lái)完成。
如何使用插件
1. 安裝需要的插件
例如:
安裝HtmlWebpackPlugin
npm install --save-dev html-webpack-plugin
2. 配置對(duì)應(yīng)的插件
webpack.config.js
// 導(dǎo)入對(duì)應(yīng)的插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [new HtmlWebpackPlugin()]
}
webpack常用的plugin
- 官網(wǎng)介紹plugins
- 第三方插件awesome-webpack
- HotModuleReplacementPlugin –開(kāi)啟全局的模塊熱替換(HMR);
- NamedModulesPlugin –當(dāng)模塊熱替換(HMR)時(shí)在瀏覽器控制臺(tái)輸出對(duì)用戶更友好的模塊名字信息;
- CommonsChunkPlugin –提取chunk公共部分;
- ExtractTextPlugin –獨(dú)立生成css文件,以外鏈的形式加載;
- UglifyJsPlugin –壓縮js;
- HtmlWebpackPlugin –使用模版生成html。
1. HtmlWebpackPlugin
HtmlWebpackPlugin
HtmlWebpackPlugin會(huì)在打包結(jié)束之后自動(dòng)創(chuàng)建一個(gè)index.html, 并將打包好的JS自動(dòng)引入到這個(gè)文件中
- 安裝HtmlWebpackPlugin
npm install --save-dev html-webpack-plugin
- 配置HtmlWebpackPlugin
webpack.config.js
// 導(dǎo)入HtmlWebpackPlugin插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [new HtmlWebpackPlugin()]
}
- HtmlWebpackPlugin高級(jí)使用
默認(rèn)情況下HtmlWebpackPlugin生成的html文件是一個(gè)空的文件, 如果想指定生成文件中的內(nèi)容可以通過(guò)配置模板的方式來(lái)實(shí)現(xiàn)
例如:
默認(rèn)情況下生成html文件并沒(méi)有壓縮, 如果想讓html文件壓縮可以設(shè)置module.exports = { plugins: [new HtmlWebpackPlugin({ // 指定打包的模板, 如果不指定會(huì)自動(dòng)生成一個(gè)空的 template: "index.html" })] }minify屬性module.exports = { plugins: [new HtmlWebpackPlugin({ minify: { // 告訴htmlplugin打包之后的html文件需要壓縮 collapseWhitespace: true, } })] }
更多高級(jí)配置請(qǐng)查看 插件文檔
2. CleanWebpackPlugin
webpack-clean-plugin會(huì)在打包之前將我們指定的文件夾清空
應(yīng)用場(chǎng)景:每次打包前將dist目錄清空, 然后再存放新打包的內(nèi)容, 避免新老混淆問(wèn)題
參考文檔
- 安裝clean-webpack-plugin
npm install --save-dev clean-webpack-plugin
-
配置clean-webpack-plugin
webpack.config.js
// 導(dǎo)入CleanWebpackPlugin插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [new CleanWebpackPlugin()]
}
3. CopyWebpackPlugin
在打包項(xiàng)目的時(shí)候除了JS/CSS/圖片/字體圖標(biāo)等需要打包以外, 可能還有一些相關(guān)的文檔也需要打包
文檔內(nèi)容是固定不變的, 我們只需要將對(duì)應(yīng)的文件拷貝到打包目錄中即可, 那么這個(gè)時(shí)候我們就可以使用copy-plugin來(lái)實(shí)現(xiàn)文件的拷貝
參考文檔
- 安裝copy-webpack-plugin
npm install --save-dev copy-webpack-plugin
-
配置copy-webpack-plugin
webpack.config.js
// 導(dǎo)入CleanWebpackPlugin插件
const CopyWebpackPlugin = require('CopyWebpackPlugin');
module.exports = {
plugins: [
new CopyWebpackPlugin([
// 從哪個(gè)文件拷貝到哪個(gè)文件
{from:"doc", to:"./doc"}
])
]
}
4. MiniCssExtractPlugin
mini-css-extract-plugin是一個(gè)專門(mén)用于將打包的CSS內(nèi)容提取到單獨(dú)文件的插件
前面我們通過(guò)style-loader打包的CSS都是直接插入到head中的
- 安裝mini-css-extract-plugin
npm install --save-dev mini-css-extract-plugin
-
配置mini-css-extract-plugin
webpack.config.js
// 導(dǎo)入 MiniCssExtractPlugin 插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: './css/[name].css',
})
]
}
-
替換style-loader
webpack.config.js
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
因?yàn)閟tyle-loader的作用是將webpack處理之后的內(nèi)容插入到HTML的HEAD代碼中, 現(xiàn)在我們是需要打包到一個(gè)單獨(dú)的文件中
【注意】
如果相關(guān)文件資源無(wú)法顯示, 需要根據(jù)打包后的結(jié)構(gòu)手動(dòng)設(shè)置公開(kāi)路徑
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
],
},
],
},
};
-
webpack打包圖片路徑問(wèn)題
webpack打包之后給我們的都是相對(duì)路徑,但是正是因?yàn)槭窍鄬?duì)路徑, 所以會(huì)導(dǎo)致在html中使用的圖片能夠正常運(yùn)行, 在css中的圖片不能正常運(yùn)行
例如: 在以下目錄中
打包之后的路徑是 images/animal.jpg
那么在html中, 會(huì)去html文件所在路徑下找images,正好能找到所以不報(bào)錯(cuò)
但是在css中, 會(huì)去css文件所在路徑下找images, 找不到所以報(bào)錯(cuò)
|---bundle
|---css
|---index.css
|---js
|---index.js
|---images
|---animal.jpg
|---index.html
解決方案
在開(kāi)發(fā)階段將publicPath設(shè)置為dev-server服務(wù)器地址
在上線階段將publicPath設(shè)置為線上服務(wù)器地址
5. 壓縮CSS代碼( OptimizeCSSAssetsPlugin 和 TerserJSPlugin )
壓縮用mini-css-extract-plugin插件打包的css代碼
參考文檔
參考文檔
- 安裝OptimizeCSSAssetsPlugin
npm install --save-dev optimize-css-assets-webpack-plugin
-
配置OptimizeCSSAssetsPlugin
webpack.config.js
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
// optimization: 配置webpack的優(yōu)化項(xiàng)
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin({})],
},
}
【注意】
由于配置了webpack的optimization.minimizer項(xiàng)目會(huì)覆蓋默認(rèn)的JS壓縮選項(xiàng), 所以JS代碼也需要通過(guò)插件自己壓縮
- 安裝TerserJSPlugin
npm install --save-dev terser-webpack-plugin
-
配置TerserJSPlugin
webpack.config.js
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
// optimization: 配置webpack的優(yōu)化項(xiàng)
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
// mode: 指定打包的模式,
// production: 上線(生產(chǎn))模式, 會(huì)對(duì)打包的JS代碼進(jìn)行壓縮
mode: "production",
}
6. 監(jiān)聽(tīng)打包文件變化( watch 和 devServer)
-
什么是watch
可以監(jiān)聽(tīng)打包文件變化,當(dāng)它們修改后會(huì)重新編譯打包
參考文檔 -
配置watch
webpack.config.js
module.exports = {
// 監(jiān)聽(tīng)打包文件變化,當(dāng)它們修改后會(huì)重新編譯打包
watch: true,
watchOptions: {
aggregateTimeout: 300, // 防抖, 和函數(shù)防抖一樣, 改變過(guò)程中不重新打包, 只有改變完成指定時(shí)間后才打包
poll: 1000, // 每隔多少時(shí)間(毫秒)檢查一次變動(dòng)
ignored: /node_modules/ // 排除一些巨大的文件夾, 不需要監(jiān)控的文件夾
},
}
-
什么是webpack-dev-server
webpack-dev-server和watch一樣可以監(jiān)聽(tīng)文件變化
webpack-dev-server可以將我們打包好的程序運(yùn)行在一個(gè)服務(wù)器環(huán)境下, 可以解決"開(kāi)發(fā)階段"的跨域問(wèn)題
參考文檔 - 安裝webpack-dev-server
npm install webpack-dev-server --save-dev
-
配置webpack-dev-server
webpack.config.js
module.exports = {
devServer: {
// 告訴devServer要把哪個(gè)目錄運(yùn)行在服務(wù)器環(huán)境下
contentBase: "./bundle",
// 編譯打包之后是否需要自動(dòng)在瀏覽器中打開(kāi), 默認(rèn)為false.
// 如果為false則需要自己在瀏覽器地址欄輸入localhost:8080
open: true,
// 端口號(hào), 默認(rèn)是8080
port: 9090
},
}
打包指令由原來(lái)的
npx webpack更改為npx webpack-dev-server
watch 和 devServer的區(qū)別
watch在修改文件后需要手動(dòng)刷新瀏覽器, devServer會(huì)自動(dòng)刷新
watch是只能在本地環(huán)境中的, devServer是可以在服務(wù)器環(huán)境中的利用webpack-dev-server代理解決跨域問(wèn)題
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能
所謂同源是指: 協(xié)議,域名,端口都相同,就是同源, 否則就是跨域
例如:
http://127.0.0.1:8080
http://127.0.0.1:8080 // 同源
http://127.0.0.1:8080
http://127.0.0.1:9090 // 跨域
webpack.config.js
module.exports = {
devServer: {
contentBase: "./bundle",
open: true,
port: 9090,
proxy: [{
context: ["/user", "/login"],
/*
target: 當(dāng)我們?cè)诖a中發(fā)送請(qǐng)求到/user的時(shí)候, devServer就會(huì)自動(dòng)將我們請(qǐng)求的地址替換為
http://127.0.0.1:3000/user
*/
target: "http://127.0.0.1:3000",
changeOrigin: true, // 域名跨域
secure: false, // https跨域
pathRewrite:{"": "/api"} // 路徑重寫(xiě), 將路徑中的api替換為空
}]
}
}
【注意】
devServer只能解決開(kāi)發(fā)階段的跨域問(wèn)題, 并不能解決項(xiàng)目上線之后的跨域問(wèn)題
因?yàn)轫?xiàng)目上線之后是將打包好的文件上傳到服務(wù)器, 而打包好的文件中并沒(méi)有devServer, 所以項(xiàng)目上線之后要想解決跨域問(wèn)題還是需要依賴后端開(kāi)發(fā)人員
通過(guò)webpack-dev-server自動(dòng)打包并沒(méi)有真正的放到指定的目錄中, 因?yàn)樽x寫(xiě)磁盤(pán)是非常耗時(shí)和消耗性能的, 所以為了提升性能webpack-dev-server將轉(zhuǎn)換好的內(nèi)容直接放到了內(nèi)存中
常用配置附錄
- target:要使用url模塊解析的url字符串
- forward:要使用url模塊解析的url字符串
- agent:要傳遞給http(s).request的對(duì)象(請(qǐng)參閱Node的https代理和http代理對(duì)象)
- ssl:要傳遞給https.createServer()的對(duì)象
- ws:true / false,是否代理websockets
- xfwd:true / false,添加x-forward標(biāo)頭
- secure:true / false,是否驗(yàn)證SSL Certs
- toProxy:true / false,傳遞絕對(duì)URL作為路徑(對(duì)代理代理很有用)
- prependPath:true / false,默認(rèn)值:true - 指定是否要將目標(biāo)的路徑添加到代理路徑
- ignorePath:true / false,默認(rèn)值:false - 指定是否要忽略傳入請(qǐng)求的代理路徑(注意:如果需要,您必須附加/手動(dòng))。
- localAddress:要為傳出連接綁定的本地接口字符串
- changeOrigin:true / false,默認(rèn)值:false - 將主機(jī)標(biāo)頭的原點(diǎn)更改為目標(biāo)URL
7. HotModuleReplacementPlugin
通過(guò)webpack-dev-server可以實(shí)現(xiàn)實(shí)時(shí)監(jiān)聽(tīng)打包內(nèi)容的變化, 每次打包之后都會(huì)自動(dòng)刷新網(wǎng)頁(yè), 但是正是因?yàn)槊慨?dāng)內(nèi)容被修改時(shí)都會(huì)自動(dòng)刷新網(wǎng)頁(yè), 所以給我們帶來(lái)了很多不便, 這時(shí)就需要通過(guò)HMR插件來(lái)優(yōu)化調(diào)試開(kāi)發(fā)
HMR(HotModuleReplacementPlugin)熱更新插件, 在內(nèi)容發(fā)生改變的時(shí)候時(shí)時(shí)的更新修改的內(nèi)容但是不會(huì)重新刷新網(wǎng)站
-
HMR使用:
HotModuleReplacementPlugin是一個(gè)內(nèi)置插件, 所以不需要任何安裝直接引入webpack模塊即可使用
const webpack = require("webpack");
module.exports = {
devServer: {
hot: true, // 開(kāi)啟熱更新, 只要開(kāi)啟了熱更新就不會(huì)自動(dòng)刷新網(wǎng)頁(yè)了
hotOnly: true // 哪怕不支持熱更新也不要刷新網(wǎng)頁(yè)
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}
【注意】
如果是通過(guò)style-loader來(lái)處理CSS, 那么經(jīng)過(guò)前面兩步就已經(jīng)實(shí)現(xiàn)了熱更新
如果是通過(guò)MiniCssExtractPlugin.loader來(lái)處理CSS, 那么還需要額外配置MiniCssExtractPlugin.loader
loader: MiniCssExtractPlugin.loader,
options:{
hmr: true
}
-
JS模塊如何實(shí)現(xiàn)熱更新
對(duì)于css模塊而言, 在css-loader中已經(jīng)幫我們實(shí)現(xiàn)了熱更新, 只要css代碼被修改就會(huì)立即更新
但是對(duì)于js模塊而言, 系統(tǒng)默認(rèn)并沒(méi)有給我們實(shí)現(xiàn)熱更新, 所以修改了js模塊代碼并不會(huì)立即更新
所以我們需要手動(dòng)監(jiān)聽(tīng)模塊變化
index.js
// 判斷當(dāng)前有沒(méi)有開(kāi)啟熱更新
if (module.hot){
// 告訴熱更新需要監(jiān)聽(tīng)哪一個(gè)JS模塊的變化
module.hot.accept("./test.js", function () {
// 手動(dòng)編寫(xiě)模塊變化后的業(yè)務(wù)邏輯
});
}
8. babel
在開(kāi)發(fā)中為了兼容一些低級(jí)版本的瀏覽器, 我們需要將ES678高級(jí)語(yǔ)法轉(zhuǎn)換為ES5低級(jí)語(yǔ)法, 否則在低級(jí)版本瀏覽器中我們的程序無(wú)法正確執(zhí)行。默認(rèn)情況下webpack是不會(huì)將我們的代碼轉(zhuǎn)換成ES5低級(jí)語(yǔ)法的, 如果需要轉(zhuǎn)換我們需要使用babel來(lái)轉(zhuǎn)換
參考文檔
- 安裝轉(zhuǎn)換到ES5的相關(guān)包
npm install --save-dev babel-loader @babel/core @babel/preset-env
- 配置babel
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 不做處理的目錄
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
}
]
}
-
presets優(yōu)化
在實(shí)際開(kāi)發(fā)中默認(rèn)情況下babel會(huì)將所有高于ES5版本的代碼都轉(zhuǎn)換為ES5代碼,但是有時(shí)候可能我們需要兼容的瀏覽器已經(jīng)實(shí)現(xiàn)了更高版本的代碼, 那么這個(gè)時(shí)候我們就不需要轉(zhuǎn)換。因?yàn)槿绻麨g覽器本身已經(jīng)實(shí)現(xiàn)了, 我們?cè)偃マD(zhuǎn)換就會(huì)增加代碼的體積,就會(huì)影響到網(wǎng)頁(yè)的性能。
所以我們通過(guò)配置presets的方式來(lái)告訴webpack我們需要兼容哪些瀏覽器,然后babel就會(huì)根據(jù)我們的配置自動(dòng)調(diào)整轉(zhuǎn)換方案, 如果需要兼容的瀏覽器已經(jīng)實(shí)現(xiàn)了, 就不轉(zhuǎn)換了
參考文檔
"presets": [["@babel/preset-env",{
targets: {
"chrome": "58",
"ie": "10"
},
}]],
利用babel實(shí)現(xiàn)低版本語(yǔ)法
對(duì)于有對(duì)應(yīng)關(guān)系的語(yǔ)法而言, 經(jīng)過(guò)我們上面的配置就已經(jīng)能夠?qū)崿F(xiàn)自動(dòng)轉(zhuǎn)換了,但是對(duì)于沒(méi)有對(duì)應(yīng)關(guān)系的語(yǔ)法而言, 經(jīng)過(guò)我們上節(jié)面的配置還不能實(shí)現(xiàn)自動(dòng)轉(zhuǎn)換。
什么叫有對(duì)應(yīng)關(guān)系, 什么叫做沒(méi)有對(duì)應(yīng)關(guān)系?
有對(duì)應(yīng)關(guān)系就是指ES5中有對(duì)應(yīng)的概念, 例如: 箭頭函數(shù)對(duì)應(yīng)普通函數(shù), let對(duì)應(yīng)var, 這個(gè)就叫做有對(duì)應(yīng)關(guān)系
沒(méi)有對(duì)應(yīng)關(guān)系就是指E5中根本就沒(méi)有對(duì)應(yīng)的語(yǔ)法, 例如Promise, includes等方法是ES678新增的,ES5中根本就沒(méi)有對(duì)應(yīng)的實(shí)現(xiàn), 這個(gè)時(shí)候就需要再增加一些額外配置, 讓babel自己幫我們實(shí)現(xiàn)對(duì)應(yīng)的語(yǔ)法
參考文檔
- 安裝實(shí)現(xiàn)包
npm install --save @babel/polyfill
-
在用到?jīng)]有對(duì)應(yīng)關(guān)系的文件中導(dǎo)入polyfill包
index.js
import "@babel/polyfill";
Promise.resolve().then(function () {
console.log("babel");
});
【注意】
如果導(dǎo)入了polyfill,那么無(wú)論我們有沒(méi)有用到?jīng)]有對(duì)應(yīng)關(guān)系的語(yǔ)法都會(huì)打包到文件中。但是這樣會(huì)增加打包后文件的大小, 我們希望的是只將用到的不存在對(duì)應(yīng)關(guān)系的語(yǔ)法打包到文件中,那么就需要在webpack.config.js中再配置一下。
"presets": [["@babel/preset-env",{
...
// 只打包沒(méi)有對(duì)應(yīng)關(guān)系的語(yǔ)法
useBuiltIns: "usage"
}]],
如果設(shè)置了useBuiltIns: "usage屬性, 那么polyfill會(huì)自動(dòng)引入, 不需要我們?cè)偈謩?dòng)導(dǎo)入
babel 的第二種配置方式
直接導(dǎo)入polyfill的方式只適用于一般項(xiàng)目開(kāi)發(fā), 但是如果是在編寫(xiě)一些第三方模塊的時(shí)候這種方式會(huì)出現(xiàn)一些問(wèn)題。因?yàn)檫@種方式是通過(guò)全局變量的方式來(lái)注入代碼, 會(huì)污染全局環(huán)境. 所以我們?cè)賮?lái)看一下polyfill的第二種配置方式
參考文檔
- 安裝相關(guān)模塊
npm install --save @babel/polyfill
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
- 配置相關(guān)信息
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false,
"version": "7.0.0-beta.0"
}
]
]
}
【注意】
"corejs": false, 還是全局注入,還是會(huì)污染全局環(huán)境
"corejs": 2, 則不會(huì)污染全局環(huán)境, 所以還需要安裝corejs2
npm install --save @babel/runtime-corejs2
babel-使用技巧
- 查看錯(cuò)誤信息
- 根據(jù)錯(cuò)誤信息查詢 文檔
- 根據(jù)文檔缺什么安裝配置什么
9. webpack-merge
為了便于維護(hù)配置文件, 我們會(huì)將開(kāi)發(fā)階段、上線階段、公共配置分為三個(gè)文件。
webpack-merge模塊就是用來(lái)實(shí)現(xiàn)重復(fù)代碼的抽離和合并進(jìn)一步優(yōu)化配置文件。
- 安裝webpack-merge
npm install --save-dev webpack-merge
- 將重復(fù)代碼抽取到 webpack.config.common.js中
-
在開(kāi)發(fā)階段配置文件和上線階段配置文件中導(dǎo)入common.js, 利用merge合并即可
例如:
webpack.config.common.js
webpack.config.dev.js
webpack.config.prod.js


