何爲插件(Plugin)?專注處理 webpack 在編譯過程中的某個特定的任務的功能模塊,可以稱爲插件。
Plugin 是一個擴展器,它豐富了 webpack 本身,針對是 loader 結束後,webpack 打包的整個過程,它並不直接操作文件,而是基於事件機制工作,會監聽 webpack 打包過程中的某些節點,執行廣泛的任務。
Plugin 的特點
是一個獨立的模塊
模塊對外暴露一個 js 函數
函數的原型
(prototype)
上定義了一個注入compiler
對象的apply
方法apply
函數中需要有通過compiler
對象掛載的webpack
事件鉤子,鉤子的回調中能拿到當前編譯的compilation
對象,如果是異步編譯插件的話可以拿到回調callback
完成自定義子編譯流程並處理
complition
對象的內部數據如果異步編譯插件的話,數據處理完成後執行
callback
回調。
下面介紹 18 個常用的 webpack 插件。
本文在gitthub做了收錄:github.com/Michael-lzg…
HotModuleReplacementPlugin
模塊熱更新插件。Hot-Module-Replacement
的熱更新是依賴於 webpack-dev-server
,後者是在打包文件改變時更新打包文件或者 reload 刷新整個頁面,HRM
是隻更新修改的部分。
HotModuleReplacementPlugin
是webpack
模塊自帶的,所以引入webpack
後,在plugins
配置項中直接使用即可。
const webpack = require('webpack')
plugins: [
new webpack.HotModuleReplacementPlugin(), // 熱更新插件
]
html-webpack-plugin
生成 html 文件。將 webpack 中entry
配置的相關入口 chunk
和 extract-text-webpack-plugin
抽取的 css 樣式 插入到該插件提供的template
或者templateContent
配置項指定的內容基礎上生成一個 html 文件,具體插入方式是將樣式link
插入到head
元素中,script
插入到head
或者body
中。
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(__dirname, '/index.html'),
minify: {
// 壓縮HTML文件
removeComments: true, // 移除HTML中的註釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true, // 壓縮內聯css
},
inject: true,
}),
]
inject 有四個選項值
true:默認值,
script
標籤位於html
文件的body
底部body:
script
標籤位於html
文件的body
底部(同 true)head:
script
標籤位於head
標籤內false:不插入生成的 js 文件,只是單純的生成一個
html
文件
多頁應用打包
有時,我們的應用不一定是一個單頁應用,而是一個多頁應用,那麼如何使用 webpack 進行打包呢。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
index: './src/index.js',
login: './src/login.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash:6].js',
},
//...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html', //打包後的文件名
}),
new HtmlWebpackPlugin({
template: './public/login.html',
filename: 'login.html', //打包後的文件名
}),
],
}
如果需要配置多個 HtmlWebpackPlugin
,那麼 filename
字段不可缺省,否則默認生成的都是 index.html
。
但是有個問題,index.html
和 login.html
會發現,都同時引入了 index.f7d21a.js
和 login.f7d21a.js
,通常這不是我們想要的,我們希望 index.html
中只引入 index.f7d21a.js
,login.html
只引入 login.f7d21a.js
。
HtmlWebpackPlugin
提供了一個 chunks
的參數,可以接受一個數組,配置此參數僅會將數組中指定的 js 引入到 html 文件中
module.exports = {
//...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html', //打包後的文件名
chunks: ['index'],
}),
new HtmlWebpackPlugin({
template: './public/login.html',
filename: 'login.html', //打包後的文件名
chunks: ['login'],
}),
],
}
這樣執行 npm run build
,可以看到 index.html
中僅引入了 index 的 js 文件,而 login.html
中也僅引入了 login 的 js 文件。
clean-webpack-plugin
clean-webpack-plugin
用於在打包前清理上一次項目生成的 bundle 文件,它會根據 output.path
自動清理文件夾;這個插件在生產環境用的頻率非常高,因爲生產環境經常會通過 hash 生成很多 bundle 文件,如果不進行清理的話每次都會生成新的,導致文件夾非常龐大。
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '/index.html'),
}),
new CleanWebpackPlugin(), // 所要清理的文件夾名稱
]
extract-text-webpack-plugin
將 css 成生文件,而非內聯 。該插件的主要是爲了抽離 css 樣式,防止將樣式打包在 js 中引起頁面樣式加載錯亂的現象
const ExtractTextPlugin = require('extract-text-webpack-plugin')
plugins: [
// 將css分離到/dist文件夾下的css文件夾中的index.css
new ExtractTextPlugin('css/index.css'),
]
mini-css-extract-plugin
將 CSS 提取爲獨立的文件的插件,對每個包含 css 的 js 文件都會創建一個 CSS 文件,支持按需加載 css 和 sourceMap
。只能用在 webpack4 中,對比另一個插件 extract-text-webpack-plugin 有以下特點:
異步加載
不重複編譯,性能更好
更容易使用
只針對 CSS
這個插件應該只用在生產環境配置,並且在 loaders
鏈中不使用 style-loader
, 而且這個插件暫時不支持 HMR
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
'postcss-loader',
'less-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[id].[contenthash:8].css',
}),
],
}
purifycss-webpack
有時候我們 css 寫得多了或者重複了,這就造成了多餘的代碼,我們希望在生產環境進行去除。
const path = require('path')
const PurifyCssWebpack = require('purifycss-webpack') // 引入PurifyCssWebpack插件
const glob = require('glob') // 引入glob模塊,用於掃描全部html文件中所引用的css
module.exports = merge(common, {
plugins: [
new PurifyCssWebpack({
paths: glob.sync(path.join(__dirname, 'src/*.html')),
}),
],
})
optimize-css-assets-webpack-plugin
我們希望減小 css 打包後的體積,可以用到 optimize-css-assets-webpack-plugin
。
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") // 壓縮css代碼
optimization: {
minimizer: [
// 壓縮css
new OptimizeCSSAssetsPlugin({})
]
}
UglifyJsPlugin
uglifyJsPlugin
是 vue-cli
默認使用的壓縮代碼方式,用來對 js 文件進行壓縮,從而減小 js 文件的大小,加速 load 速度。它使用的是單線程壓縮代碼,打包時間較慢,所以可以在開發環境將其關閉,生產環境部署時再把它打開。
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
plugins: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: true, //是否啓用文件緩存
parallel: true //使用多進程並行運行來提高構建速度
})
ParallelUglifyPlugin
開啓多個子進程,把對多個文件壓縮的工作分別給多個子進程去完成,每個子進程其實還是通過 UglifyJS
去壓縮代碼,但是變成了並行執行。
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
plugins: [
new ParallelUglifyPlugin({
//cacheDir 用於配置緩存存放的目錄路徑。
cacheDir: '.cache/',
sourceMap: true,
uglifyJS: {
output: {
comments: false,
},
compress: {
warnings: false,
},
},
}),
]
terser-webpack-plugin
Webpack4.0 默認是使用 terser-webpack-plugin
這個壓縮插件,在此之前是使用 uglifyjs-webpack-plugin
,兩者的區別是後者對 ES6 的壓縮不是很好,同時我們可以開啓 parallel
參數,使用多進程壓縮,加快壓縮。
const TerserPlugin = require('terser-webpack-plugin') // 壓縮js代碼
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1
cache: true, // 是否緩存
sourceMap: false,
}),
]
}
NoErrorsPlugin
報錯但不退出 webpack 進程。編譯出現錯誤時,使用 NoEmitOnErrorsPlugin
來跳過輸出階段。這樣可以確保輸出資源不會包含錯誤。
plugins: [new webpack.NoEmitOnErrorsPlugin()]
compression-webpack-plugin
所有現代瀏覽器都支持 gzip
壓縮,啓用 gzip
壓縮可大幅縮減傳輸資源大小,從而縮短資源下載時間,減少首次白屏時間,提升用戶體驗。
gzip 對基於文本格式文件的壓縮效果最好(如:CSS、JavaScript 和 HTML),在壓縮較大文件時往往可實現高達 70-90% 的壓縮率,對已經壓縮過的資源(如:圖片)進行 gzip 壓縮處理,效果很不好。
const CompressionPlugin = require('compression-webpack-plugin')
plugins: [
new CompressionPlugin({
// gzip壓縮配置
test: /\.js$|\.html$|\.css/, // 匹配文件名
threshold: 10240, // 對超過10kb的數據進行壓縮
deleteOriginalAssets: false, // 是否刪除原文件
}),
]
當然,這個方法還需要後端配置支持。
DefinePlugin
我們可以通過 DefinePlugin
可以定義一些全局的變量,我們可以在模塊當中直接使用這些變量,無需作任何聲明,DefinePlugin
是 webpack
自帶的插件。
plugins: [
new webpack.DefinePlugin({
DESCRIPTION: 'This Is The Test Text.',
}),
]
// 直接引用
console.log(DESCRIPTION)
ProvidePlugin
自動加載模塊。任何時候,當 identifier
被當作未賦值的變量時, module 就會自動被加載,並且 identifier
會被這個 module 輸出的內容所賦值。這是 webpack 自帶的插件。
module.exports = {
resolve: {
alias: {
jquery: './lib/jquery',
},
},
plugins: [
//提供全局的變量,在模塊中使用無需用require引入
new webpack.ProvidePlugin({
$: 'jquery',
React: 'react',
}),
],
}
DLLPlugin
這是在一個額外的獨立的 webpack 設置中創建一個只有 dll 的 bundle(dll-only-bundle)
。這個插件會生成一個名爲 manifest.json
的文件,這個文件是用來讓 DLLReferencePlugin
映射到相關的依賴上去的。
「使用步驟如下」
1、在 build 下創建 webpack.dll.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
vendor: [
'vue-router',
'vuex',
'vue/dist/vue.common.js',
'vue/dist/vue.js',
'vue-loader/lib/component-normalizer.js',
'vue',
'axios',
'echarts',
],
},
output: {
path: path.resolve('./dist'),
filename: '[name].dll.js',
library: '[name]_library',
},
plugins: [
new webpack.DllPlugin({
path: path.resolve('./dist', '[name]-manifest.json'),
name: '[name]_library',
}),
// 建議加上代碼壓縮插件,否則dll包會比較大。
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
},
}),
],
}
2、在 webpack.prod.conf.js
的 plugin 後面加入配置
new webpack.DllReferencePlugin({
manifest: require('../dist/vendor-manifest.json'),
})
3、package.json
文件中添加快捷命令(build:dll)
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js",
"build:dll": "webpack --config build/webpack.dll.conf.js"
}
生產環境打包的時候先npm run build:dll
命令會在打包目錄下生成 vendor-manifest.json
文件與 vendor.dll.js 文件。然後npm run build
生產其他文件。
4、根目錄下的入口 index.html
加入引用
<script type="text/javascript" src="./vendor.dll.js"></script>
HappyPack
HappyPack
能讓 webpack 把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。要注意的是 HappyPack
對 file-loader
、url-loader
支持的不友好,所以不建議對該 loader 使用。
1、HappyPack 插件安裝
npm i -D happypack
2、webpack.base.conf.js
文件對 module.rules 進行配置
module: {
rules: [
{
test: /\.js$/,
use: ['happypack/loader?id=babel'],
include: [resolve('src'), resolve('test')],
exclude: path.resolve(__dirname, 'node_modules'),
},
{
test: /\.vue$/,
use: ['happypack/loader?id=vue'],
},
]
}
3、在生產環境 webpack.prod.conf.js
文件進行配置
const HappyPack = require('happypack')
// 構造出共享進程池,在進程池中包含5個子進程
const HappyPackThreadPool = HappyPack.ThreadPool({ size: 5 })
plugins: [
new HappyPack({
// 用唯一的標識符id,來代表當前的HappyPack是用來處理一類特定的文件
id: 'babel',
// 如何處理.js文件,用法和Loader配置中一樣
loaders: ['babel-loader?cacheDirectory'],
threadPool: HappyPackThreadPool,
}),
new HappyPack({
id: 'vue', // 用唯一的標識符id,來代表當前的HappyPack是用來處理一類特定的文件
loaders: [
{
loader: 'vue-loader',
options: vueLoaderConfig,
},
],
threadPool: HappyPackThreadPool,
}),
]
「注意,當項目較小時,多線程打包反而會使打包速度變慢。」
copy-webpack-plugin
我們在 public/index.html
中引入了靜態資源,但是打包的時候 webpack 並不會幫我們拷貝到 dist 目錄,因此 copy-webpack-plugin
就可以很好地幫我做拷貝的工作了。
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'public/js/*.js',
to: path.resolve(__dirname, 'dist', 'js'),
flatten: true,
},
],
}),
],
}
IgnorePlugin
這是 webpack 內置插件,它的作用是:忽略第三方包指定目錄,讓這些指定目錄不要被打包進去。
比如我們要使用 moment
這個第三方依賴庫,該庫主要是對時間進行格式化,並且支持多個國家語言。雖然我設置了語言爲中文,但是在打包的時候,是會將所有語言都打包進去的。這樣就導致包很大,打包速度又慢。對此,我們可以用 IgnorePlugin
使得指定目錄被忽略,從而使得打包變快,文件變小。
const Webpack = require('webpack')
plugins: [
//moment這個庫中,如果引用了./locale/目錄的內容,就忽略掉,不會打包進去
new Webpack.IgnorePlugin(/\.\/locale/, /moment/),
]
我們雖然按照上面的方法忽略了包含’./locale/'
該字段路徑的文件目錄,但是也使得我們使用的時候不能顯示中文語言了,所以這個時候可以手動引入中文語言的目錄。
import moment from 'moment'
//手動引入所需要的語言包
import 'moment/locale/zh-cn'
moment.locale('zh-cn')
let r = moment().endOf('day').fromNow()
console.log(r)
推薦文章
搭建一個 vue-cli4+webpack 移動端框架(開箱即用)
從零構建到優化一個類似vue-cli的腳手架
封裝一個toast和dialog組件併發布到npm
從零開始構建一個webpack項目
總結幾個webpack打包優化的方法
總結vue知識體系之高級應用篇
總結vue知識體系之實用技巧
總結vue知識體系之基礎入門篇
❝作者:lzg9527
https://segmentfault.com/a/1190000022956602
❞
1. JavaScript 重溫系列(22篇全)
2. ECMAScript 重溫系列(10篇全)
3. JavaScript設計模式 重溫系列(9篇全)4. 正則 / 框架 / 算法等 重溫系列(16篇全)5. Webpack4 入門(上)|| Webpack4 入門(下)6. MobX 入門(上) || MobX 入門(下)7. 59篇原創系列彙總回覆“加羣”與大佬們一起交流學習~點這,與大家一起分享本文吧~