插件(plugins
)
插件是
webpack
的支柱功能。插件目的在于解決loader
無(wú)法實(shí)現(xiàn)的其他事。
webpack
插件是一個(gè)具有 apply
屬性的 JavaScript
對(duì)象。apply
屬性會(huì)被 webpack compiler
調(diào)用,并且 compiler
對(duì)象可在整個(gè)編譯生命周期訪問(wèn)。如ConsoleLogOnBuildWebpackPlugin.js
:
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
//compiler hook 的 tap 方法的第一個(gè)參數(shù),
//應(yīng)該是駝峰式命名的插件名稱(chēng)
compiler.hooks.run.tap(pluginName, compilation => {
console.log("webpack 構(gòu)建過(guò)程開(kāi)始!");
});
}
}
一些插件簡(jiǎn)介:
Name | Description |
---|---|
AggressiveSplittingPlugin | 將原來(lái)的 chunk 分成更小的 chunk |
BabelMinifyWebpackPlugin | 使用 babel-minify進(jìn)行壓縮 |
BannerPlugin | 在每個(gè)生成的 chunk 頂部添加 banner |
CommonsChunkPlugin | 提取 chunks 之間共享的通用模塊 |
CompressionWebpackPlugin | 預(yù)先準(zhǔn)備的資源壓縮版本,使用 Content-Encoding 提供訪問(wèn)服務(wù) |
ContextReplacementPlugin | 重寫(xiě) require 表達(dá)式的推斷上下文 |
CopyWebpackPlugin | 將單個(gè)文件或整個(gè)目錄復(fù)制到構(gòu)建目錄 |
DefinePlugin | 允許在編譯時(shí)(compile time)配置的全局常量 |
DllPlugin | 為了極大減少構(gòu)建時(shí)間,進(jìn)行分離打包 |
EnvironmentPlugin | DefinePlugin 中 process.env 鍵的簡(jiǎn)寫(xiě)方式。 |
ExtractTextWebpackPlugin | 從 bundle 中提取文本(CSS)到單獨(dú)的文件 |
HotModuleReplacementPlugin | 啟用模塊熱替換(Enable Hot Module Replacement - HMR) |
HtmlWebpackPlugin | 簡(jiǎn)單創(chuàng)建 HTML 文件,用于服務(wù)器訪問(wèn) |
I18nWebpackPlugin | 為 bundle 增加國(guó)際化支持 |
IgnorePlugin | 從 bundle 中排除某些模塊 |
LimitChunkCountPlugin | 設(shè)置 chunk 的最小/最大限制,以微調(diào)和控制 chunk |
LoaderOptionsPlugin | 用于從 webpack 1 遷移到 webpack 2 |
MinChunkSizePlugin | 確保 chunk 大小超過(guò)指定限制 |
NoEmitOnErrorsPlugin | 在輸出階段時(shí),遇到編譯錯(cuò)誤跳過(guò) |
NormalModuleReplacementPlugin | 替換與正則表達(dá)式匹配的資源 |
NpmInstallWebpackPlugin | 在開(kāi)發(fā)時(shí)自動(dòng)安裝缺少的依賴(lài) |
ProvidePlugin | 不必通過(guò) import/require 使用模塊 |
SourceMapDevToolPlugin | 對(duì) source map進(jìn)行更細(xì)粒度的控制 |
EvalSourceMapDevToolPlugin | 對(duì) eval source map 進(jìn)行更細(xì)粒度的控制 |
UglifyjsWebpackPlugin | 可以控制項(xiàng)目中 UglifyJS 的版本 |
ZopfliWebpackPlugin | 通過(guò) node-zopfli 將資源預(yù)先壓縮的版本 |
更多第三方插件,請(qǐng)查看 awesome-webpack 列表。
用法案例:CommonsChunkPlugin
The CommonsChunkPlugin
已經(jīng)從webpack v4 legato
中移除。想要了解在最新版本中如何處理chunk
,請(qǐng)查看SplitChunksPlugin
(該插件配置過(guò)程更為便捷高效)。
以
element-ui
初始化工程為例看下vendor.js
:
import Vue from 'vue'
import ElementUI from 'element-ui'
由于插件可以攜帶參數(shù)/選項(xiàng),你必須在 webpack
配置中,向 plugins
屬性傳入 new
實(shí)例。根據(jù)你的 webpack
用法,這里有多種方式使用插件。
//package.json中引入依賴(lài)
"devDependencies": {
...
"html-webpack-plugin": "^2.24.1",
"webpack": "^2.4.1",
...
}
//webpack.config.js中引入
//通過(guò) npm 直接安裝插件
const HtmlWebpackPlugin
= require('html-webpack-plugin');
//訪問(wèn)webpack內(nèi)置的插件
//CommonsChunkPlugin就是內(nèi)置插件之一
const webpack = require('webpack');
功能:該插件用于提取
chunks
之間共享的通用模塊。使用CommonsChunkPlugin
從「應(yīng)用程序bundle
」中提取vendor 引用
(vendor reference
) 到vendor bundle
,并把引用vendor
的部分替換為__webpack_require__()
調(diào)用
Node API
(O(∩_∩)O~,這個(gè)是啥)
「
TODO
」即便使用Node API
,用戶(hù)也應(yīng)該在配置中傳入plugins
屬性。compiler.apply
并不是推薦的使用方式。
//some-node-script.js
//訪問(wèn) webpack 運(yùn)行時(shí)(runtime)
const webpack = require('webpack');
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
compiler.apply(new webpack.ProgressPlugin());
compiler.run(function(err, stats) {
// ...
});
你知道嗎:以上看到的示例和
webpack
自身運(yùn)行時(shí)(runtime
) 極其類(lèi)似。wepback
源碼中隱藏有大量使用示例,你可以用在自己的配置和腳本中。
官網(wǎng)插件
HtmlWebpackPlugin
根據(jù)配置的入口和出口,自動(dòng)生成
index.html
,且會(huì)自動(dòng)引入相關(guān)的依賴(lài)。
- 如果你想要了解更多
HtmlWebpackPlugin
插件提供的全部功能和選項(xiàng),那么你就應(yīng)該多多熟悉HtmlWebpackPlugin
倉(cāng)庫(kù)。 - 你還可以看一下
html-webpack-template
,除了默認(rèn)模板之外,還提供了一些額外的功能。
CleanWebpackPlugin
- 你可能已經(jīng)注意到,由于過(guò)去的指南和代碼示例遺留下來(lái),導(dǎo)致我們的
/dist
文件夾相當(dāng)雜亂。 -
webpack
會(huì)生成文件,然后將這些文件放置在/dist
文件夾中,但是webpack
無(wú)法追蹤到哪些文件是實(shí)際在項(xiàng)目中用到的。 - 通常,在每次構(gòu)建前清理
/dist
文件夾,是比較推薦的做法,因此只會(huì)生成用到的文件。讓我們完成這個(gè)需求。 -
clean-webpack-plugin
是一個(gè)比較普及的管理插件,讓我們安裝和配置下。
npm install clean-webpack-plugin --save-dev
-
webpack.config.js
中配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
plugins: [
+ new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Output Management'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
- 現(xiàn)在執(zhí)行
npm run build
,再檢查/dist
文件夾。如果一切順利,你現(xiàn)在應(yīng)該不會(huì)再看到舊的文件,只有構(gòu)建后生成的文件!
WebpackManifestPlugin
該插件能夠在項(xiàng)目根目錄生成一份
manifest.json
的文件,通過(guò)該文件的映射關(guān)系可以讓我們知道webpack
是如何追蹤所有模塊并映射到輸出bundle
中的。
//安裝插件:
npm install --save-dev webpack-manifest-plugin
//在 webpack.config.js中引入,大致如下:
var ManifestPlugin = require('webpack-manifest-plugin');
module.exports = {
// ...
plugins: [
new ManifestPlugin()
// ManifestPlugin方法可以接受一些選項(xiàng)參數(shù)options,如
// new ManifestPlugin(options)
]
};
//3.可選參數(shù)如下:
options.fileName 表示要生成文件的名稱(chēng),默認(rèn)為manifest.json
options.publicPath 表示生成映射文件的路徑,默認(rèn)為output.publicPath
還有其他的參數(shù)見(jiàn)官網(wǎng)
https://github.com/danethurber/webpack-manifest-plugin
HotModuleReplacementPlugin
開(kāi)發(fā)環(huán)境啟用
HMR
:?jiǎn)⒂么斯δ軐?shí)際上相當(dāng)簡(jiǎn)單。而我們要做的,就是更新webpack-dev-server
的配置,和使用webpack
內(nèi)置的HMR
插件
IgnorePlugin
import
或require
調(diào)用時(shí),忽略以下正則表達(dá)式匹配的模塊:
// requestRegExp 匹配(test)資源請(qǐng)求路徑的正則表達(dá)式。
// contextRegExp (可選)匹配(test)資源上下文(目錄)的正則表達(dá)式。
new webpack.IgnorePlugin(
requestRegExp, [contextRegExp]
)
舉例:moment
包
比如我們要使用moment
這個(gè)第三方依賴(lài)庫(kù),該庫(kù)主要是對(duì)時(shí)間進(jìn)行格式化,并且支持多個(gè)國(guó)家語(yǔ)言。
import moment from 'moment'
//設(shè)置語(yǔ)言
moment.locale('zh-cn');
let r = moment().endOf('day').fromNow();
console.log(r);
- 這樣打印出來(lái)的就是中文的時(shí)間,因?yàn)槲以谑褂眠@個(gè)
API
的時(shí)候,前面設(shè)置了語(yǔ)言類(lèi)型moment.locale
為中文zh-cn
。 - 但是,雖然我設(shè)置了語(yǔ)言為中文,但是在打包的時(shí)候,是會(huì)將所有語(yǔ)言都打包進(jìn)去的。這樣就導(dǎo)致包很大,打包速度又慢
- 所以,最好能夠少打包一些沒(méi)用的依賴(lài)目錄進(jìn)去
- 而
moment
的包含./locale/
該字段路徑的文件目錄就是各國(guó)語(yǔ)言的目錄,比如./locale/zh-cn
就是中文語(yǔ)言 - 使用
IgnorePlugin
let webpack = require('webpack');
plugins:[
//moment這個(gè)庫(kù)中,如果引用了./locale/目錄的內(nèi)容,
//就忽略掉,不會(huì)打包進(jìn)去
new webpack.IgnorePlugin(/\.\/locale/,/moment/),
]
- 我們雖然按照上面的方法忽略了包含
./locale/
該字段路徑的文件目錄,但是也使得我們使用的時(shí)候不能顯示中文語(yǔ)言了,所以這個(gè)時(shí)候可以手動(dòng)引入中文語(yǔ)言的目錄
import moment from 'moment'
//手動(dòng)引入所需要的語(yǔ)言包
import 'moment/locale/zh-cn';
moment.locale('zh-cn');
let r = moment().endOf('day').fromNow();
console.log(r);
- 這樣就OK了。既能夠顯示中文,又把不必要的語(yǔ)言包都忽略打包了
DllPlugin
DLLPlugin
和DLLReferencePlugin
用某種方法實(shí)現(xiàn)了拆分bundles
,同時(shí)還大大提升了構(gòu)建的速度(dll
是一種最簡(jiǎn)單粗暴并且極其有效的優(yōu)化方式)。
- 用
Webpack
打包的時(shí)候,對(duì)于一些不經(jīng)常更新的第三方庫(kù),比如react
,lodash
,我們希望能和自己的代碼分離開(kāi),Webpack
社區(qū)有兩種方案
CommonsChunkPlugin
和DLLPlugin
- 對(duì)于
CommonsChunkPlugin
:-
webpack
每次打包實(shí)際還是需要去處理這些第三方庫(kù),只是打包完之后,能把第三方庫(kù)和我們自己的代碼分開(kāi)。 - 且每次工程重新打包,
verdors
也會(huì)重新打包,打包的chunkhash
就會(huì)改變,導(dǎo)致每次發(fā)布,vendors
的緩存都會(huì)失效。這樣增加了用戶(hù)的流量消耗和首屏加載時(shí)間。
-
- 而
DLLPlugin
則是能把第三方代碼完全分離開(kāi),即每次只打包項(xiàng)目自身的代碼。引入的第三方包沒(méi)有改變就不需要重新打包。 DLLPlugin
具體用法:
/**
要使用 DLLPlugin,需要額外新建一個(gè)配置文件。
所以對(duì)于用這種方式打包的項(xiàng)目,
一般會(huì)有下面兩個(gè)配置文件:
webpack.config.js
webpack.dll.config.js
先來(lái)看下 webpack.dll.config.js
*/
const webpack = require('webpack')
const library = '[name]_lib'
const path = require('path')
module.exports = {
entry: {
vendors: ['react', 'lodash']
//上面的vendors,看到有人有的libs
},
output: {
filename: '[name].dll.js',
path: 'dist/',
library
},
plugins: [
new webpack.DllPlugin({
//執(zhí)行的上下文環(huán)境,對(duì)之后DllReferencePlugin有用
context: __dirname,
path: path.join(
__dirname, 'dist/[name]-manifest.json'
),
// This must match the output.library option above
name: library
}),
],
}
//再改下 webpack.config.js 文件
const webpack = require('webpack')
module.exports = {
entry: {
app: './src/index'
},
output: {
filename: 'app.bundle.js',
path: 'dist/',
},
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
//下面的vendors有人用libs
manifest: require('./dist/vendors-manifest.json')
})
]
}
/**
manifest: require('./dist/vendors-manifest.json')
這里的路徑要和 webpack.dll.config.js 里面的對(duì)應(yīng)。
然后運(yùn)行:
*/
$ webpack --config webpack.dll.config.js
$ webpack --config webpack.config.js
//然后你的 html 文件像下面這樣引用
<script src="/dist/vendors.dll.js"></script>
<script src="/dist/app.bundle.js"></script>
/**
上面的用法也存在一些不方便的地方,
比如在 webpack.config.js 中要明確
指出對(duì)應(yīng)的 manifest.json 文件。
還有當(dāng) DLL 需要更新的時(shí)候,
比如 react 升級(jí)了,或者加入新的第三方庫(kù),
都需要手動(dòng)像下面這樣編譯一次。
*/
$ webpack --config webpack.dll.config.js
/**
---非官方插件(不需關(guān)注,沒(méi)有緩存作用了吧?)----
因?yàn)樯厦孢@些問(wèn)題,
所以基于官方的 DllReferencePlugin,
有一個(gè) Dll Link Plugin。鏈接如下:
www.npmjs.com/package/dll-link-webpack-plugin
使用這個(gè)插件,
只需要對(duì) webpack.config.js 作下小小的改動(dòng)
*/
const webpack = require('webpack')
const DllLinkPlugin = require('dll-link-webpack-plugin')
module.exports = {
// ...
plugins: [
new DllLinkPlugin({
config: require('webpack.dll.config.js')
})
]
}
/**
直接替換掉 DllReferencePlugin,
然后傳入對(duì)應(yīng)的 DLL 配置文件就可以了。
每次打包的時(shí)候,只需要運(yùn)行
*/
$ webpack --config webpack.config.js
/**
上面的命令便會(huì)自動(dòng)生成對(duì)應(yīng)的 vendors 文件,
需要更新的時(shí)候,也會(huì)自動(dòng)更新。
*/
SplitChunksPlugin
在默認(rèn)情況下,
SplitChunksPlugin
僅僅影響按需加載的代碼塊,因?yàn)楦某跏級(jí)K會(huì)影響HTML文件應(yīng)包含的腳本標(biāo)記以運(yùn)行項(xiàng)目。-
webpack
將根據(jù)以下條件自動(dòng)拆分代碼塊:- 會(huì)被共享的代碼塊或者 node_mudules 文件夾中的代碼塊
- 體積大于30KB的代碼塊(在gz壓縮前)
- 按需加載代碼塊時(shí)的并行請(qǐng)求數(shù)量不超過(guò)5個(gè)
- 加載初始頁(yè)面時(shí)的并行請(qǐng)求數(shù)量不超過(guò)3個(gè)
//舉例: // 文件一:impvue.js import 'vue' ... // 文件二:index.js // 動(dòng)態(tài)加載 impvue.js import('./impvue') ... /** 打包之后的結(jié)果會(huì)創(chuàng)建一個(gè)包含 vue 的獨(dú)立代碼塊, 當(dāng)包含 impvue.js 的原始代碼塊被調(diào)用時(shí), 這個(gè)獨(dú)立代碼塊會(huì)并行請(qǐng)求進(jìn)來(lái)。 原因: 1) vue 來(lái)自 node_modules 文件夾 2) vue 體積超過(guò)30KB 3) 導(dǎo)入調(diào)用時(shí)的并行請(qǐng)求數(shù)為2 4) 不影響頁(yè)面初始加載 */
我們這樣做的原因是因?yàn)椋?code>vue代碼并不像你的業(yè)務(wù)代碼那樣經(jīng)常變動(dòng),把它單獨(dú)提取出來(lái)就可以和你的業(yè)務(wù)代碼分開(kāi)緩存,極大的提高效率。
SplitChunksPlugin
的默認(rèn)配置
splitChunks: {
/**
chunks屬性用來(lái)選擇分割哪些代碼塊,
可選值有:
'all'(所有代碼塊)
'async'(按需加載的代碼塊)
'initial'(初始化代碼塊)
*/
chunks: "async",
minSize: 30000, // 模塊的最小體積
minChunks: 1, // 模塊的最小被引用次數(shù)
maxAsyncRequests: 5, // 按需加載的最大并行請(qǐng)求數(shù)
maxInitialRequests: 3, // 一個(gè)入口最大并行請(qǐng)求數(shù)
automaticNameDelimiter: '~', // 文件名的連接符
name: true,
/**
緩存組是個(gè)有趣的功能:
在默認(rèn)設(shè)置中,
會(huì)將`node_mudules`中的模塊打包進(jìn)`vendors`,
引用超過(guò)兩次的模塊分配到`default` `bundle` 中。
更可以通過(guò) `priority` 來(lái)設(shè)置優(yōu)先級(jí)。
*/
cacheGroups: { // 緩存組
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
- 在項(xiàng)目中添加
SplitChunksPlugin
/**
為了方便演示,我們先安裝兩個(gè)類(lèi)庫(kù): lodash 和 axios,
npm i lodash axios -S
修改 main.js,引入 lodash 和axios 并調(diào)用相應(yīng)方法:
*/
import Modal from './components/modal/modal'
import './assets/style/common.less'
import _ from 'lodash'
import axios from 'axios'
const App = function () {
let div = document.createElement('div')
div.setAttribute('id', 'app')
document.body.appendChild(div)
let dom = document.getElementById('app')
let modal = new Modal()
dom.innerHTML = modal.template({
title: '標(biāo)題',
content: '內(nèi)容',
footer: '底部'
})
}
const app = new App()
console.log(_.camelCase('Foo Bar'))
axios.get('aaa')
/**
-----------------------------
使用SplitChunksPlugin不需要安裝任何依賴(lài),
只需在 webpack.config.js 中的 config對(duì)象
添加 optimization 屬性:
*/
optimization: {
splitChunks: {
chunks: 'initial',
automaticNameDelimiter: '.',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: 1
}
}
},
//一般都配合runtimeChunkPlugin使用
runtimeChunk: {
name: entrypoint => `manifest.${entrypoint.name}`
}
}
/**
配置 runtimeChunk 會(huì)給每個(gè)入口添加一個(gè)只包含runtime的額外的代碼塊,
name 的值也可以是字符串,不過(guò)這樣就會(huì)給每個(gè)入口添加相同的 runtime,
配置為函數(shù)時(shí),返回當(dāng)前的entry對(duì)象,即可分入口設(shè)置不同的runtime。
*/
/**
我們?cè)侔惭b一個(gè) webpack-bundle-analyzer,
這個(gè)插件會(huì)清晰的展示出打包后的各個(gè)bundle所依賴(lài)的模塊:
引入:
*/
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin
//使用,在plugins數(shù)組中添加即可:
new BundleAnalyzerPlugin()
- 可以看看
Webpack4
之SplitChunksPlugin
這里提及的問(wèn)題
BannerPlugin
為每個(gè)
chunk
文件頭部添加banner
(注釋/版權(quán))。
new webpack.BannerPlugin(options)
//options選項(xiàng):
{
banner: string, // 其值為字符串,將作為注釋存在
raw: boolean, // 如果值為 true,將直出,不會(huì)被作為注釋
entryOnly: boolean, // 如果值為 true,將只在入口 chunks 文件中添加
test: string | RegExp | Array,
include: string | RegExp | Array,
exclude: string | RegExp | Array,
}
CopyWebpackPlugin
:拷貝靜態(tài)文件
將單個(gè)文件或整個(gè)目錄直接從源目錄拷貝到構(gòu)建目錄.
//config:配置了一些參數(shù)
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
},
//上面的static目錄先不管,
//我們先看這個(gè),文件中是一些客戶(hù)可自行修改的配置
//獨(dú)立出來(lái),方便修改
{
from: path.resolve(config.directory.root, 'sysconfig.js'),
to: 'sysconfig.js'
},
])
打包好的工程下:
ExtractTextWebpackPlugin
:分離CSS
文件
MiniCssExtractPlugin
這個(gè)呢?
const ExtractTextPlugin
= require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
/**
我看我們的工程中是這樣配置的
具體的后續(xù)再好好看下:
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
*/
]
}
它會(huì)將所有的入口
chunk(entry chunks)
中引用的*.css
,移動(dòng)到獨(dú)立分離的CSS
文件。因此,你的樣式將不再內(nèi)嵌到JS bundle
中,而是會(huì)放到一個(gè)單獨(dú)的CSS
文件(即styles.css
)當(dāng)中。 如果你的樣式文件大小較大,這會(huì)做更快提前加載,因?yàn)?CSS bundle
會(huì)跟JS bundle
并行加載。
再來(lái)看個(gè)CSS優(yōu)化
(壓縮)相關(guān)的另一個(gè)插件optimize-css-assets-webpack-plugin
:
/**這個(gè)插件可以接受下列配置(均為可選):
assetNameRegExp: 正則,用于匹配需要優(yōu)化的資源名。默認(rèn)值是 /\.css$/g
cssProcessor: 用于壓縮和優(yōu)化CSS 的處理器,默認(rèn)是 cssnano.這是一個(gè)函數(shù),
應(yīng)該按照 cssnano.process 接口(接受一個(gè)CSS和options參數(shù),返回一個(gè)Promise)
canPrint: {bool} 表示插件能夠在console中打印信息,默認(rèn)值是true
*/
// webpack4+
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// webpack4
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourcMap: true
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: { safe: true, discardComments: { removeAll: true } },
canPrint: true
}),
],
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
]
};
//webpack3中一般配合 ExtractTextPlugin一起使用。
UglifyjsWebpackPlugin
此插件使用
uglify-js
進(jìn)行js
文件的壓縮。
- 下面說(shuō)明是來(lái)自:關(guān)閉開(kāi)發(fā)環(huán)境的代碼壓縮
UglifyJsPlugin
- 開(kāi)發(fā)環(huán)境是不需要去壓縮代碼,主要是因?yàn)樘男阅芰耍啃薷囊粋€(gè)地方就要花幾秒去等待頁(yè)面渲染,非常浪費(fèi)開(kāi)發(fā)時(shí)間,解決辦法就是配置不同的環(huán)境變量
- 去在開(kāi)發(fā)環(huán)境的時(shí)候不要這個(gè)
UglifyJsPlugin
插件,為此,webpack4
又增加了新的Mode
,且默認(rèn)值是production
;而且更新后的webpack
默認(rèn)是有UglifyJsPlugin
這個(gè)配置的 - 也就是說(shuō)在不設(shè)置任何環(huán)境變量的情況下,始終會(huì)有壓縮代碼的行為出現(xiàn),導(dǎo)致編譯極其耗時(shí),那我的解決辦法就是在
package.json
文件啟動(dòng)時(shí)設(shè)置環(huán)境變量:
image
Tree Shaking
:一個(gè)術(shù)語(yǔ),通常用于描述移除JavaScript
上下文中的未引用代碼(dead-code)
。新的webpack 4
正式版本,擴(kuò)展了這個(gè)檢測(cè)能力,通過(guò)package.json
的sideEffects
屬性作為標(biāo)記,向compiler
提供提示,表明項(xiàng)目中的哪些文件是pure(純的 ES2015 模塊)
,由此可以安全地刪除文件中未使用的部分。
- 這種方式是通過(guò)
package.json
的sideEffects
屬性來(lái)實(shí)現(xiàn)的。
{
"name": "your-project",
"sideEffects": false
}
- 如果你的代碼確實(shí)有一些副作用,那么可以改為提供一個(gè)數(shù)組:
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}
- 從
webpack 4
開(kāi)始,也可以通過(guò)mode
配置選項(xiàng)輕松切換到壓縮輸出,只需設(shè)置為production
。 - 配置
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
mode: "production"
};
DefinePlugin
DefinePlugin
允許創(chuàng)建一個(gè)在編譯時(shí)可以配置的全局常量。這可能會(huì)對(duì)開(kāi)發(fā)模式和發(fā)布模式的構(gòu)建允許不同的行為非常有用。如果在開(kāi)發(fā)構(gòu)建中,而不在發(fā)布構(gòu)建中執(zhí)行日志記錄,則可以使用全局常量來(lái)決定是否記錄日志。這就是DefinePlugin
的用處,設(shè)置它,就可以忘記開(kāi)發(fā)和發(fā)布構(gòu)建的規(guī)則。
new webpack.DefinePlugin({
/**
在vue-cli創(chuàng)建的項(xiàng)目中,
凡是src下的文件,都可以訪問(wèn)到下面這些變量,如VERSION,
例如main.js,App.vue等等
*/
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
//根據(jù)process.env.NODE_ENV區(qū)分環(huán)境,引入配置文件
process.env: {NODE_ENV: "development"},
})
EnvironmentPlugin
: 是一個(gè)通過(guò)DefinePlugin
來(lái)設(shè)置process.env
環(huán)境變量的快捷方式。
new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG'])
//上面的寫(xiě)法和下面這樣使用 DefinePlugin 的效果相同:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
})
HashedModuleIdsPlugin
該插件會(huì)根據(jù)模塊的相對(duì)路徑生成一個(gè)四位數(shù)的
hash
作為模塊id
, 建議用于生產(chǎn)環(huán)境。
//其他具體說(shuō)明再看看
new webpack.HashedModuleIdsPlugin({
hashFunction: 'sha256',
hashDigest: 'hex',
hashDigestLength: 20
})
ZipPlugin
(好像官網(wǎng)沒(méi)列)
發(fā)布的時(shí)候可以使用 壓縮插件將資源(圖片,配置文件等)壓縮成一個(gè)
.zip
文件
//參數(shù)中配置了需要將工程打包
if (config.build.productionToZip) {
const ZipPlugin = require('zip-webpack-plugin')
webpackConfig.plugins.push(
new ZipPlugin({
path: config.build.assetsZipRoot,
filename: 'build.zip',
extension: 'zip',
})
)
}
ModuleConcatenationPlugin
-
webpack2
處理后的每個(gè)模塊均被一個(gè)函數(shù)包裹,如下:
/* 50 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
window.lib = {}
...
/* harmony default export */ __webpack_exports__["a"] = (window.lib);
/***/ }),
- 這樣會(huì)帶來(lái)一個(gè)問(wèn)題:降低瀏覽器中
JS
執(zhí)行效率,這主要是閉包函數(shù)降低了JS
引擎解析速度。 - 于是webpack團(tuán)隊(duì)參考
Closure Compiler
和Rollup JS
,將一些有聯(lián)系的模塊,放到一個(gè)閉包函數(shù)里面去,通過(guò)減少閉包函數(shù)數(shù)量從而加快JS
的執(zhí)行速度。 -
webpack3
通過(guò)設(shè)置ModuleConcatenationPlugin
使用這個(gè)新特性:
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
}
- 記住,此插件僅適用于由
webpack
直接處理的ES6
模塊。在使用轉(zhuǎn)譯器(transpiler
)時(shí),你需要禁用對(duì)模塊的處理(例如Babel
中的modules
選項(xiàng))。
NpmInstallWebpackPlugin
在開(kāi)發(fā)時(shí)自動(dòng)安裝缺少的依賴(lài):
package.json
中需要配置了才會(huì)自動(dòng)安裝哦
//安裝
npm install --save-dev npm-install-webpack-plugin
//用法
//不要忘記引入哦
const NpmInstallPlugin = require('npm-install-webpack-plugin')
//插件配置:
plugins: [
new NpmInstallPlugin()
],
//相當(dāng)于:
plugins: [
new NpmInstallPlugin({
// 使用 --save 或者 --save-dev
dev: false,
// 安裝缺少的 peerDependencies
peerDependencies: true,
// 減少控制臺(tái)日志記錄的數(shù)量
quiet: false,
// npm command used inside company, yarn is not supported yet
npm: 'tnpm'
});
],
//可以提供一個(gè) Function 來(lái)動(dòng)態(tài)設(shè)置 dev:
plugins: [
new NpmInstallPlugin({
dev: function(module, path) {
return [
"babel-preset-react-hmre",
"webpack-dev-middleware",
"webpack-hot-middleware",
].indexOf(module) !== -1;
},
}),
],
ProvidePlugin
自動(dòng)加載模塊,而不必到處
import
或require
。
//要自動(dòng)加載 jquery,我們可以將兩個(gè)變量都指向?qū)?yīng)的 node 模塊:
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
//然后在我們?nèi)我庠创a中:
$('#item'); // <= 起作用
jQuery('#item'); // <= 起作用
//使用:Lodash Map
new webpack.ProvidePlugin({
_map: ['lodash', 'map']
})
//使用:Vue.js
new webpack.ProvidePlugin({
Vue: ['vue/dist/vue.esm.js', 'default']
})
LimitChunkCountPlugin
當(dāng)你在編寫(xiě)代碼時(shí),可能已經(jīng)添加了許多代碼分離點(diǎn)(
code split point
)來(lái)實(shí)現(xiàn)按需加載(load stuff on demand
)。在編譯完之后,你可能會(huì)注意到有一些很小的chunk
- 這產(chǎn)生了大量HTTP
請(qǐng)求開(kāi)銷(xiāo)。幸運(yùn)的是,此插件可以通過(guò)合并的方式,后處理你的chunk
,以減少請(qǐng)求數(shù)。
//改善`chunk`傳輸?shù)牟寮?
//LimitChunkCountPlugin
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 5, // 必須大于或等于 1
minChunkSize: 1000
})
/**MinChunkSizePlugin:
通過(guò)合并小于 minChunkSize 大小的 chunk,
將 chunk 體積保持在指定大小限制以上。
*/
new webpack.optimize.MinChunkSizePlugin({
// Minimum number of characters
minChunkSize: 10000
})
/**
AggressiveSplittingPlugin可以將 bundle拆分成更小的chunk,
直到各個(gè) chunk 的大小達(dá)到 option 設(shè)置的 maxSize。
它通過(guò)目錄結(jié)構(gòu)將模塊組織在一起。
*/
new webpack.optimize.AggressiveSplittingPlugin({
minSize: 30000, // 字節(jié),分割點(diǎn)。默認(rèn):30720
maxSize: 50000, // 字節(jié),每個(gè)文件最大字節(jié)。默認(rèn):51200
chunkOverhead: 0, // 默認(rèn):0
entryChunkMultiplicator: 1, // 默認(rèn):1
})
SourceMapDevToolPlugin
生成source map
的兩種配置
- 配置
devtool
//最簡(jiǎn)單的生成source map的方式是,如下配置webpack.config.js,
const path = require('path');
module.exports = {
devtool: 'source-map',
entry: {
index: path.resolve(__dirname, 'src/index.js'),
},
output: {
devtoolModuleFilenameTemplate: '[resource-path]',
path: path.resolve(__dirname, 'dist/'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
query: { presets: ['@babel/preset-env'] }
}
},
]
},
};
/**
其中,devtool默認(rèn)值為false,
配置為source-map表示以獨(dú)立的文件形式生成source map。
因此,dist/ 文件夾下,會(huì)產(chǎn)生兩個(gè)文件,
index.js
index.js.map
index.js文件末尾,webpack會(huì)自動(dòng)添加一行注釋?zhuān)?//# sourceMappingURL=index.js.map
瀏覽器解析到這里,會(huì)自動(dòng)根據(jù)index.js的相對(duì)路徑,請(qǐng)求map文件并加載它。
*/
SourceMapDevToolPlugin
/**
除了直接配置devtool之外,
還可以使用webpack官方插件SourceMapDevToolPlugin,
進(jìn)行更細(xì)粒度的source map配置。
*/
const path = require('path');
const webpack = require('webpack');
module.exports = {
// devtool: 'source-map',
entry: {
index: path.resolve(__dirname, 'src/index.js'),
},
output: {
// devtoolModuleFilenameTemplate: '[resource-path]',
path: path.resolve(__dirname, 'dist/'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
query: { presets: ['@babel/preset-env'] }
}
},
]
},
plugins: [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
moduleFilenameTemplate: '[resource-path]',
append: '\n//# sourceMappingURL=http://127.0.0.1:8080/dist/[url]'
}),
],
};
/**
以上配置中,我們注釋掉了
devtool和devtoolModuleFilenameTemplate,
將它們配置到了SourceMapDevToolPlugin中。
這樣配置,dist/目錄最后也會(huì)生成兩個(gè)文件,
index.js
index.js.map
由于我們使用了該插件的append功能,
修改了sourceMappingURL地址,
因此,index.js末尾source map文件的地址就變成了,
//# sourceMappingURL=http://127.0.0.1:8080/dist/index.js.map
*/
- 注:這里值得一提的是
同時(shí)配置devtool和SourceMapDevToolPlugin是不行的,
index.js文件末尾會(huì)被添加兩行sourceMappingURL,
//# sourceMappingURL=http://127.0.0.1:8080/dist/index.js.map
//# sourceMappingURL=index.js.map
而且map文件的內(nèi)容也不正確,是一個(gè)空的map文件,
-
EvalSourceMapDevToolPlugin
:對(duì) eval source map 進(jìn)行更細(xì)粒度的控制 -
webpack——devtool
里的7
種SourceMap
模式
我們先來(lái)看看文檔對(duì)這7
種模式的解釋?zhuān)?/li>
模式 | 解釋 |
---|---|
eval | 每個(gè)module會(huì)封裝到 eval 里包裹起來(lái)執(zhí)行,并且會(huì)在末尾追加注釋 //@ sourceURL. |
source-map | 生成一個(gè)SourceMap文件. |
hidden-source-map | 和 source-map 一樣,但不會(huì)在 bundle 末尾追加注釋. |
inline-source-map | 生成一個(gè) DataUrl 形式的 SourceMap 文件. |
eval-source-map | 每個(gè)module會(huì)通過(guò)eval()來(lái)執(zhí)行,并且生成一個(gè)DataUrl形式的SourceMap |
cheap-source-map | 生成一個(gè)沒(méi)有列信息(column-mappings)的SourceMaps文件,不包含loader的 sourcemap(譬如 babel 的 sourcemap) |
cheap-module-source-map | 生成一個(gè)沒(méi)有列信息(column-mappings)的SourceMaps文件,同時(shí) loader 的 sourcemap 也被簡(jiǎn)化為只包含對(duì)應(yīng)行的。 |
- 注1:webpack 不僅支持這 7 種,
而且它們還是可以任意組合上面的eval、inline、hidden關(guān)鍵字,
就如文檔所說(shuō),你可以設(shè)置 souremap 選項(xiàng)為:
cheap-module-inline-source-map。
- 注2:如果你的modules里面已經(jīng)包含了SourceMaps,
你需要用source-map-loader 來(lái)和合并生成一個(gè)新的 SourceMaps。
還有很多其他的,需要時(shí)自行搜索學(xué)習(xí)即可
......