Webpack知識整理———插件
- 概述
- 基本使用
- Webpack 常用插件
- 自動清除輸出目錄
- 自動生成參考打包結果的HTML
- 把靜態資源目錄拷貝到打包后的目錄中
- 插件作業原理
- 提高開發體驗
- 自動編譯
- webpack-dev-server
- webpack5之后 webpack-dev-server 執行cli 修改為 webpack serve
- 使用contentBase 指定靜態資源目錄
- 利用devServer 在開發環境下啟用代理
- 使用Source Map來除錯代碼
- source map 基本使用
- 選擇不同的模式
- 自動重繪的問題
- 開啟HMR ( 熱更新 )
- 生產環境優化
- 1. 組態檔根據環境不同匯出不同配置
- 2. 一個環境對應一個組態檔
- Webpack DefinePlugin
- Tree Sharking
- webpack 合并模塊
- sideEffects
- 代碼分割
- 多入口打包
- 動態匯入
- webpack MiniCssExtractPlugin
- optimize-css-assets-webpack-plugin
概述
在開發中為了增強webpack自動化的能力,我們需要安裝一些插件來輔助開發,
Loader 是為了解決資源加載的問題,而Plugin是為了解決其他自動化的問題,
基本使用
在webpack.config.js 檔案中添加 plugins屬性 該屬性是一個陣列,在這個陣列填入插件的實體即可,
plugins: [
插件1,
插件2,
…
]
Webpack 常用插件
自動清除輸出目錄
clean-webpack-plugin 通過安裝使用該插件 來實作打包時自動清除上一次打包成果的功能
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins:[
new CleanWebpackPlugin ()
]
}
自動生成參考打包結果的HTML
如果html檔案是由我們自己手動引入打包后的資源,那么會存在硬編碼問題,即,如果修改打包配置修改檔案名或者檔案路徑,就需要我們手動再去修改html中的檔案參考
我們可以借助 html-webpack-plugin 來實作自動生成html 并自動完成打包后檔案的注入,這樣再運行打包之后,dist目錄中就會出現html檔案 并且自動引入對應的js檔案了
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins:[
new HtmlWebpackPlugin()
]
}
我們可以自定義輸出的html檔案內容,可以在new HtmlWebpackPlugin中添加配置物件,如果需要自定義的內容比較復雜,我們還可以使用template設定模板
plugins:[
new HtmlWebpackPlugin({
title:'Hello World!',
meta: {
viewport: 'width=device-width'
},
template:'./src/index'
})
]
如果要輸出多個頁面就繼續添加 多個實體即可
plugins:[
new HtmlWebpackPlugin({
title:'Hello World!',
meta: {
viewport: 'width=device-width'
},
filename:'home.html',
template:'./src/index'
}),
new HtmlWebpackPlugin({
filename:'about.html'
}),
]
把靜態資源目錄拷貝到打包后的目錄中
專案中的靜態資源目錄,比如 public 目錄 想要在打包后也放在打包后的目錄中,可以借助于這個 copy-webpack-plugin 插件來完成
const CopyWebpackPlugin = require('copy-webpack-plugin')
plugins:[
new CopyWebpackPlugin({
patterns:[ { from:'目標',to:'生成到哪去' } ]
})
]
插件作業原理
webpack的插件作業機制就是我們在軟體開發中常見的鉤子機制來實作,
webpack要插件必須是一個函式,或者包含apply方法的物件,
我們來實作一個去掉 /******/ 這種注釋的插件
class MyPlugin {
apply (compiler) {
console.log('MyPlugin 啟動')
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation => 可以理解為此次打包的背景關系
for (const name in compilation.assets) {
if (name.endsWith('.js')) {
const contents = compilation.assets[name].source()
const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
compilation.assets[name] = {
source: () => withoutComments,
size: () => withoutComments.length
}
}
}
})
}
}
提高開發體驗
總結一下,我們使用webpack進行專案打包的流程:

四個步驟,依次進行,但是這種手動的方式過于原始,也會讓開發的體驗很差,我們就需要想辦法提高開發的體驗,使一些作業能夠自動完成,讓我們專注于代碼撰寫上,
希望能夠有以下幾個方向的優化,來提高開發體驗:
- 以 Http Server 來運行專案,而不是檔案的方式打開
- 修改完代碼保存后,能夠自動編譯 + 自動重繪頁面
- 為了方便代碼除錯,還希望擁有 Source Map 支持 這樣就可以進行斷點除錯
自動編譯
使用watch 作業模式,監聽檔案變化,自動重新打包,
執行 webpack --watch 這樣webpack就會開始監聽檔案變化了
webpack-dev-server
該工具提供一個 HTTP Server,并且這個工具集成了 「自動編譯」和「自動重繪頁面」 等功能
- 啟動一個HTTP Server 服務器運行專案
- 以watch的方式運行打包,監聽檔案變化 自動打包編譯
- 代碼更新后會自動重繪頁面
- devServer 打包生成的檔案存在記憶體中,HTTP Server也是從記憶體中獲取檔案進行執行
- 因為生成的檔案在記憶體中 所有減少了 磁盤讀寫 提高了執行效率
運行方式: webpack-dev-server
可以在 npm script 中添加該執行 并傳入cli 引數
"scripts": {
"dev": "webpack-dev-server --open --host 127.0.0.1 --port 8888"
}
然后執行 npm run dev 即可
webpack5之后 webpack-dev-server 執行cli 修改為 webpack serve
需要執行 webpack serve 指令來啟動
在package.json應做如下配置
"dev": "webpack serve"
再執行npm run start就正常啟動,
使用contentBase 指定靜態資源目錄
我們在開發的時候 CopyWebpackPlugin 一般只在最后添加 所以為了防止dev server訪問不到靜態資源
為 dev-server 指定要訪問的靜態資源目錄,在 webpack.config.js中添加
devServer: {
contentBase: './public'
}
利用devServer 在開發環境下啟用代理
前端在發起介面請求時,由于瀏覽器的同源問題,會出現跨域問題,我們在開發的程序中可以利用devServer配置代理api 來繞開同源策略,當專案上線之后 交給運維人員去配置反向代理
devServer: {
contentBase: './public',
proxy: {
// 匹配以 /api 開頭的請求
'/api': {
// http://localhost:8080/api/users -> https://api.github.com/api/users
target: 'https://api.github.com',
// 重寫地址,不希望其中出現/api
// http://localhost:8080/api/users -> https://api.github.com/users
pathRewrite: {
'^/api': ''
},
// 不能使用 localhost:8080 作為請求 GitHub 的主機名
// changeOrigin默認是false:請求頭中host仍然是瀏覽器發送過來的host
// 如果設定成true:發送請求頭中host會設定成target
changeOrigin: true
}
}
},
使用Source Map來除錯代碼
因為運行代碼都是經過打包編譯之后的,跟撰寫的不同,不方便我們除錯,Source Map就解決了這個問題 ,它利用map檔案 映射出原代碼,方便開發人員進行除錯,
webpack配置SourceMap
source map 基本使用
devtool:"source-map"
選擇不同的模式
- 開發環境下 可以使用 cheap-module-eval-source-map 模式
注意在webpack 5 之下 該模式更名為 eval-cheap-module-source-map,
也就是要這樣設定
devtool:"eval-cheap-module-source-map"
- 生成環境下 可以使用 none 這樣不會使我的開發原代碼暴露
自動重繪的問題
devServer可以在我們修改過代碼后,自動重繪頁面,但是這樣也存在一個問題,如果頁面中有一些表單元素,輸入內容到一般,修改代碼自動重繪就會出現輸入的內容丟失,就又需要再次輸入比較麻煩,
我們需要解決這個問題,這里可以使用到的就是 Hot Module Replacement (模塊熱更新)
HMR 指的是在程式運行程序中可以實時的替換掉某個模塊,并且程式的運行狀態不受影響
開啟HMR ( 熱更新 )
熱更新集成在了 webpack-dev-server 中 不需要額外安裝其他模塊,
可以通過cli 引數 運行 webpack serve --hot添加 --hot來開啟
也可以通過組態檔開啟, 這里注意 修改的樣式檔案要是js 檔案中引入的樣式檔案 這樣才會觸發熱更新
注意 : 針對于 html js 圖片檔案 需要手動去設定hmr的邏輯,社區還提供許多其他 loader 和示例,可以使 HMR 與各種框架和庫平滑地進行互動,
"scripts": {
"serve":"webpack serve --hot --open"
},
生產環境優化
1. 組態檔根據環境不同匯出不同配置
webpack的組態檔除了支持匯出一個物件,還支持匯出一個函式,函式接收兩個形參: env 和 argv, 在這個函式中 回傳我們需要的配置物件,
env 是我們通過cli(終端指令)傳遞的環境名引數
argv 是指我們運行cli程序中傳遞的所有引數
默認約定 生產環境的env的值是 “production” 比如 webpack --env production
module.exports = (env, argv) => {
const config = "具體配置物件"
if (env === 'production') {
config.mode = 'production'
config.devtool = false
config.plugins = [
...config.plugins,
new CleanWebpackPlugin(),
new CopyWebpackPlugin(['public'])
]
}
return config
}
2. 一個環境對應一個組態檔
- 創建一個公共的組態檔 webpack.common.js
- 創建開發模式下的組態檔 webpack.dev.js
- 創建生產環境下的組態檔 webpack.pro.js
- 在開發和生產 環境對應的檔案中使用
webpack-merge來合并公共配置
const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devtool: 'cheap-eval-module-source-map',
devServer: {
hot: true,
contentBase: 'public'
}
})
- 在scripts中配置對應的cli
"scripts": {
"build": "webpack --config webpack.prod.js",
"serve": "webpack serve --hot"
},
Webpack DefinePlugin
這是webpack內置的一個插件,它可以為我們的代碼注入全域成員
比如 我要注入api的公共域名
const webpack = require('webpack')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
// 值要求的是一個代碼片段
// API_BASE_URL 就可以在全域使用了
API_BASE_URL: JSON.stringify('https://api.example.com')
})
]
}
Tree Sharking
顧名思義 “搖樹” 去掉枯葉,在webpack里它的作用是「搖掉」代碼中未參考的部分,未參考代碼( dead-code ),生產模式中,webpack會自動檢測未參考的代碼 然后移除掉它們,
Tree Sharking 不是指某一個配置選項,它是一組功能搭配使用過后的優化效果,在生產模式中webpack會自動啟動它
如果需要在開發模式中使用,我們可以通過兩個配置來實作
- usedExports — 負責標記 「枯樹葉」
- minimize — 負責 「搖掉」它們
optimization: {
usedExports: true,
minimize: true
}
tree sharking 要想生效,關鍵點在于 由webpack 打包的代碼必須使用ESM,而我們有時候使用的babel版本比較低的話 就會把ESM 轉化為 commonJS 規范 這樣就會導致 tree sharking 失效,但是在最新版的babel中已經不存在這個問題了
webpack 合并模塊
concatenateModules 合并模塊,原本webpack打包會把一個模塊生產一個函式,使用了concatenateModules 可以把所有的模塊都合并到一個函式中,進一步壓縮體積,
sideEffects
開啟了sideEffects配置后,webpack在打包時就會先檢查當前代碼所屬的package.json中有沒有sideEffects的標識,以此來判斷這個模塊是不是又副作用,如果這個模塊沒有副作用,這些沒被用到的模塊就不會被打包,(這個特性在production模式下會自動開啟)
副作用: 模塊執行時除了匯出成員之外所作的事情,
sideEffects 一般用于 npm 包標記是否有副作用
比如說 一個模塊中的代碼 除了匯出成員之外,還為Object 擴展了原型方法那么這就是副作用,我們需要標記這些有副作用的檔案模塊,不然它們是不會被webpack打包進來的,
使用sideEffects的前提就是確定你的代碼真的沒有副作用,否則的話,在webpack打包時,就會誤刪掉那些有副作用的代碼,
代碼分割
webpack 可以把模塊代碼打包成一個檔案,這樣也存在一些弊端,如果應用非常復雜,模塊很多,那么最終生成的檔案就會特別的大,然而,在實際情況中,并不是每個模塊在啟動時都是必要的,
最理想的情況是,把代碼分離到多個檔案中,分包,根據需要按需加載,
當然這樣有人會有疑問,就是說一開始模塊就是分離的,既然要分離干脆別打包啊?
理由很簡單,單一檔案太大這樣肯定不行,而如果檔案數量太多太碎,也是一樣不行的,
我們可以按照不同的業務功能來對模塊進行分包,實作的方式有以下兩種:
- 多入口打包
- 動態匯入
多入口打包
一般適用于傳統的多頁應用程式,最常見的是一個頁面對應一個打包入口,公共部分單獨提取,
- entry 的值要修改成物件,每一個屬性就對應一個打包入口
- output 中filename 的 [name] 就是entry里的屬性名
- HtmlWebpackPlugin默認會在生成的html檔案中引入所有打包好的js檔案,這里我們需要單獨引入對應的js檔案 所以需要添加chunks屬性 里面的值就是要引入的包名
module.exports = {
mode: 'none',
entry: {
index: './src/index.js',
album: './src/album.js'
},
output: {
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Multi Entry',
template: './src/index.html',
filename: 'index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
title: 'Multi Entry',
template: './src/album.html',
filename: 'album.html',
chunks: ['album']
})
]
}
公共模塊提取
optimization: {
splitChunks: {
// 自動提取所有公共模塊到單獨 bundle
chunks: 'all'
}
},
動態匯入
動態匯入的模塊會被自動分包,
而實作動態匯入模塊的方法,其實就是利用ESM中動態匯入模塊的寫法即可,只要使用動態匯入的模塊webpack就會自動實作打包后生成多個分包檔案,并提取公共部分
import(./posts/posts').then(({ default: posts }) => {
})
而默認打包后的檔案名字只是在前面添加了序號,我們可以在動態匯入里添加一行魔法注釋,這樣生成的分包檔案的名字就會使用注釋中的名字了,并且添加了相同注釋的動態匯入會被打包到一起,
這一點在我們開發vue專案時,配置動態路由的時候尤為明顯,
import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => {
})
webpack MiniCssExtractPlugin
如果專案中的css檔案體積過大,我們可以將css從js中抽離出來 還是以單獨檔案的形式引入,這就需要使用 MiniCssExtractPlugin這個插件了
module: {
rules: [
{
test: /\.css$/,
use: [
// 'style-loader', // 將樣式通過 style 標簽注入
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
}
optimize-css-assets-webpack-plugin
webpack 生產模式下 只能對js檔案進行壓縮,如果需要對樣式檔案進行壓縮,就需要借助于插件
optimization: {
minimizer: [
new OptimizeCssAssetsWebpackPlugin()
]
},
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/275490.html
標籤:其他
上一篇:modal實作按鈕倒計時
