1、webpack 配置很多,這里我們探討比較經常需要修改的:
-
不復雜,可以在 configWebpack 中操作:
- mode
- devtool
-
配置復雜,可以在 chainWebpack 中操作:
- module.rules
- plugins
- optimization
2、使用 vue inspect 可以查看當前 webpack 配置
vue inspect > output.js
配置無非增刪改查四個操作,接下來一一列舉
一、增
- 增加規則
// loader 默認是從下往上處理
// enforce: 決定現有規則調用順序
// - pre 優先處理
// - normal 正常處理(默認)
// - inline 其次處理
// - post 最后處理
module.exports = {
chainWebpack: config => {
config.module
.rule('lint') // 定義一個名叫 lint 的規則
.test(/\.js$/) // 設置 lint 的匹配正則
.pre() // 指定當前規則的調用優先級
.include // 設置當前規則的作用目錄,只在當前目錄下才執行當前規則
.add('src')
.end()
.use('eslint') // 指定一個名叫 eslint 的 loader 配置
.loader('eslint-loader') // 該配置使用 eslint-loader 作為處理 loader
.options({ // 該 eslint-loader 的配置
rules: {
semi: 'off'
}
})
.end()
.use('zidingyi') // 指定一個名叫 zidingyi 的 loader 配置
.loader('zidingyi-loader') // 該配置使用 zidingyi-loader 作為處理 loader
.options({ // 該 zidingyi-loader 的配置
rules: {
semi: 'off'
}
})
config.module
.rule('compile')
.test(/\.js$/)
.include
.add('src')
.add('test')
.end()
.use('babel')
.loader('babel-loader')
.options({
presets: [
['@babel/preset-env', { modules: false }]
]
})
}
}
最后將解析為如下配置:
{
module: {
rules: [
/* config.module.rule('lint') */
{
test: /\.js$/,
enforce: 'pre',
include: ['src'],
use: [
/* config.module.rule('lint').use('eslint') */
{
loader: 'eslint-loader',
options: {
rules: {
semi: 'off'
}
}
},
/* config.module.rule('lint').use('zidingyi') */
{
loader: 'zidingyi-loader',
options: {
rules: {
semi: 'off'
}
}
}
]
},
/* config.module.rule('compile') */
{
test: /\.js$/,
include: ['src', 'test'],
use: [
/* config.module.rule('compile').use('babel') */
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { modules: false }]
]
}
}
]
}
]
}
}
- 增加插件
module.exports = {
chainWebpack: config => {
// 用法:
// config
// .plugin(name)
// .use(WebpackPlugin, args)
// 示例:
config
.plugin('clean') // 創建一個名稱為 clean 的插件
.use(CleanPlugin, [['dist'], { root: '/dir' }]) // 不要用 new 去創建插件,因為已經為你做好了
}
}
- 增加 optimization.minimizers
// 用法(v5版本可用):
config.optimization
.minimizer(name)
.use(WebpackPlugin, args)
// 示例:
config.optimization
.minimizer('css')
.use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }])
二、刪
- 刪除單個規則中的全部 loader
// vue.config.js
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')
// 清除已有規則 svg 的所有 loader。
// 如果你不這樣做,接下來的 loader 會附加在該規則現有的 loader 之后。
svgRule.uses.clear()
// 添加要替換的 loader
svgRule
.use('vue-svg-loader')
.loader('vue-svg-loader')
}
}
- 刪除單個規則中的一個loader
// 刪除前:
{
test: /\.m?jsx?$/,
exclude: [
function () { /* omitted long function */ }
],
use: [
/* config.module.rule('js').use('cache-loader') */
{
loader: 'cache-loader',
options: {
cacheDirectory: 'D:\\webproject\\webapp-jy\\node_modules\\.cache\\babel-loader',
cacheIdentifier: '519fc596'
}
},
/* config.module.rule('js').use('babel-loader') */
{
loader: 'babel-loader'
}
]
}
// 刪除后:
const jsRule = config.module.rule('js')
// 刪除 cache-loader
jsRule.uses.delete('cache-loader')
{
test: /\.m?jsx?$/,
exclude: [
function () { /* omitted long function */ }
],
use: [
/* config.module.rule('js').use('babel-loader') */
{
loader: 'babel-loader'
}
]
}
- 刪除單個規則
const moduleRule = config.module
// 刪除命名為 js 的規則
moduleRule.rules.delete('js')
- 刪除插件
config.plugins.delete(name)
- 刪除 optimization.minimizers
config.optimization.minimizers.delete(name)
三、改
- 修改單個 loader 的配置
// 兩種寫法都可修改
config.module
.rule('compile')
.use('babel')
.tap(options => merge(options, {
plugins: ['@babel/plugin-proposal-class-properties']
}))
config.module
.rule('compile')
.use('babel')
.loader('babel-loader')
.tap(options => merge(options, {
plugins: ['@babel/plugin-proposal-class-properties']
}))
- 在多個 loader 中進行添加或者刪除
config.module.rule('js').toConfig():
// {
// test: /\.m?jsx?$/,
// exclude: [ [Function] ],
// use: [
// { loader: 'cache-loader', options: [Object] },
// { loader: 'babel-loader' }
// ]
// }
// 這里以添加一個新的 loader 為例
// 1、拿到原來的 loader
let originUse = config.module.rule('js').toConfig().use
// 2、添加 loader
let newLoader = { loader: 'eslint-loader' }
originUse.splice(1, 0, newLoader)
// 3、清空原來的 loader
config.module.rule('js').uses.clear()
// 4、重新設置新的 loader
config.module.rule('js').merge({ use: originUse})
// {
// test: /\.m?jsx?$/,
// exclude: [ [Function] ],
// use: [
// { loader: 'cache-loader', options: [Object] },
// { loader: 'eslint-loader' },
// { loader: 'babel-loader' }
// ]
// }
- 修改插件參數
// 用法:
config.plugin(name)
.tap(args => newArgs)
// 示例:
config.plugin('env')
.tap(args => [...args, 'SECRET_KEY'])
// 修改前:
/* config.plugin('optimize-css') */
new OptimizeCssnanoPlugin(
{
sourceMap: false,
cssnanoOptions: {
preset: [
'default',
{
mergeLonghand: false,
cssDeclarationSorter: false
}
]
},
}
)
// 修改后:
config.plugin('optimize-css')
.tap(args => {
args[0].yy = 1
return args
})
/* config.plugin('optimize-css') */
new OptimizeCssnanoPlugin(
{
sourceMap: false,
cssnanoOptions: {
preset: [
'default',
{
mergeLonghand: false,
cssDeclarationSorter: false
}
]
},
yy: 2
}
)
config.plugin('optimize-css')
.tap(args => {
return [...args, { x: 1 }]
})
/* config.plugin('optimize-css') */
new OptimizeCssnanoPlugin(
{
sourceMap: false,
cssnanoOptions: {
preset: [
'default',
{
mergeLonghand: false,
cssDeclarationSorter: false
}
]
}
},
{
x: 1
}
)
- 修改插件實例
config.plugin(name)
.init((Plugin, args) => new Plugin(...args))
- 修改插件調用順序
- 指定當前插件上下文應該在另一個指定插件之前/之后執行,你不能在同一個插件上同時使用 .before() 和 .after()
// 用法:
// 在 otherName 之前調用
config
.plugin(name)
.before(otherName)
// 在 otherName 之后調用
config
.plugin(name)
.after(otherName)
// 示例:
// 修改之前:
[
/* config.plugin('named-chunks') */
new NamedChunksPlugin(
function () { /* omitted long function */ }
),
/* config.plugin('copy') */
new CopyWebpackPlugin(
[]
)
]
// 修改后:
config.plugin('named-chunks')
.after('copy')
[
/* config.plugin('copy') */
new CopyWebpackPlugin(
[]
),
/* config.plugin('named-chunks') */
new NamedChunksPlugin(
function () { /* omitted long function */ }
)
]
config.plugin('copy')
.before('named-chunks')
[
/* config.plugin('copy') */
new CopyWebpackPlugin(
[]
),
/* config.plugin('named-chunks') */
new NamedChunksPlugin(
function () { /* omitted long function */ }
)
]
- 修改 optimization
// 用法(v5版本可用):
config.optimization
.minimizer(name)
.tap(args => newArgs)
// 示例:
config.optimization
.minimizer('css')
.tap(args => [...args, { cssProcessorOptions: { safe: false } }])
四、查
- 使用
.toConfig()
輸出配置
// 示例1:
console.log('js config :>> ', config.module.rule('js').toConfig())
js config :>>
{
test: /\.m?jsx?$/,
exclude: [ [Function] ],
use: [
{ loader: 'cache-loader', options: [Object] },
{ loader: 'babel-loader' }
]
}
// ***************************
// 示例2:
console.log('cache-loader config :>> ', config.module.rule('js').use('cache-loader').toConfig())
cache-loader config :>>
{
loader: 'cache-loader',
options: {
cacheDirectory: 'D:\\webproject\\myproject\\node_modules\\.cache\\babel-loader',
cacheIdentifier: '20a97f42'
}
}
// ***************************
// 示例3:
console.log('plugin config :>> ', config.plugin('optimize-css').toConfig())
plugin config :>> OptimizeCssnanoPlugin {
options: { sourceMap: false, cssnanoOptions: { preset: [Array] } }
}
五、操作方法
chainWebpack 中,有兩個大的數據分類:ChainedMap
和 ChainedSet
在操作的時候分別有如下方法可以鏈式調用:
- ChainedMap:
// 從 Map 移除所有 配置.
clear()
// 通過鍵值從 Map 移除單個配置.
// key: *
delete(key)
// 獲取 Map 中相應鍵的值
// key: *
// returns: value
get(key)
// 獲取 Map 中相應鍵的值
// 如果鍵在Map中不存在,則ChainedMap中該鍵的值會被配置為fn的返回值.
// key: *
// fn: Function () -> value
// returns: value
getOrCompute(key, fn)
// 配置Map中 已存在的鍵的值
// key: *
// value: *
set(key, value)
// Map中是否存在一個配置值的特定鍵,返回 真或假
// key: *
// returns: Boolean
has(key)
// 返回 Map中已存儲的所有值的數組
// returns: Array
values()
// 返回Map中全部配置的一個對象, 其中 鍵是這個對象屬性,值是相應鍵的值,
// 如果Map是空,返回 `undefined`
// 使用 `.before() 或 .after()` 的ChainedMap, 則將按照屬性名進行排序。
// returns: Object, undefined if empty
entries()
// 提供一個對象,這個對象的屬性和值將 映射進 Map。
// 你也可以提供一個數組作為第二個參數以便忽略合並的屬性名稱。
// obj: Object
// omit: Optional Array
merge(obj, omit)
// 對當前配置上下文執行函數。
// handler: Function -> ChainedMap
// 一個把ChainedMap實例作為單個參數的函數
batch(handler)
// 條件執行一個函數去繼續配置
// condition: Boolean
// whenTruthy: Function -> ChainedMap
// 當條件為真,調用把ChainedMap實例作為單一參數傳入的函數
// whenFalsy: Optional Function -> ChainedMap
// 當條件為假,調用把ChainedMap實例作為單一參數傳入的函數
when(condition, whenTruthy, whenFalsy)
- ChainedSet:
// 添加/追加 給Set末尾位置一個值.
// value: *
add(value)
// 添加 給Set開始位置一個值.
// value: *
prepend(value)
// 移除Set中全部值.
clear()
// 移除Set中一個指定的值.
// value: *
delete(value)
// 檢測Set中是否存在一個值.
// value: *
// returns: Boolean
has(value)
// 返回Set中值的數組.
// returns: Array
values()
// 連接給定的數組到 Set 尾部。
// arr: Array
merge(arr)
// 對當前配置上下文執行函數。
// handler: Function -> ChainedSet
// 一個把 ChainedSet 實例作為單個參數的函數
batch(handler)
// 條件執行一個函數去繼續配置
// condition: Boolean
// whenTruthy: Function -> ChainedSet
// 當條件為真,調用把 ChainedSet 實例作為單一參數傳入的函數
// whenFalsy: Optional Function -> ChainedSet
// 當條件為假,調用把 ChainedSet 實例作為單一參數傳入的函數
when(condition, whenTruthy, whenFalsy)
更多配置操作,參考中文文檔
chainWebpack 中文文檔