Webpack.merge插件

webpack-merge插件可以用来merge两个webpack的配置。它的底层引用了lodash中的merge,但针对webpack的特点,专门对loaders,preLoaders,postLoaders专门做了处理。

一般情况下,对于以下两个module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var module: {
loaders: [
{
test: /\.css$/,
loaders: ['style', 'css'],
},
]
}
var moduleExtend = {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel?stage=1',
include: path.join(ROOT_PATH, 'app'),
},
]
}

进行merge(module, moduleExtend)后会得到如下结果:

1
2
3
4
5
6
7
8
9
moduleMerged = {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel?stage=1',
include: path.join(ROOT_PATH, 'app'),
},
]
}

因为loaders是数组,所以结果中的css的loader信息丢失了。所以为了保证merge之后,loaders,preLoaders,postLoaders中的内容不会丢失,webpack-merge中对于loaders,preLoaders,postLoaders进行了特殊处理,采用concat对数组内容进行合并

1
2
3
4
if (isLoader(key)) {
return b.concat(a);
}
return a.concat(b);

如此,merge的结果为

1
2
3
4
5
6
7
8
9
10
11
loaders: [
{
test: /\.css$/,
loaders: ['style', 'css'],
},
{
test: /\.jsx?$/,
loader: 'babel?stage=1',
include: path.join(ROOT_PATH, 'app'),
}
]

但是如果遇到两个test值相同的loader时,仅仅用concat的话,会产生两条结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
merge({
loaders: [{
test: /\.js$/,
loader: 'babel'
}]
}, {
loaders: [{
test: /\.js$/,
loader: 'coffee'
}]
});
// 结果为
{
loaders: [{
test: /\.js$/,
loader: 'coffee'
},
{
test: /\.js$/,
loader: 'babel'
}]
}

这时对于js文件会先用babel-loader处理,然后再用coffee-loader处理。但是更多的,我们希望merge的结果是,后一个可以覆盖掉前一个。此外,在loaders中,除了可以写loader:’coffee’,还会有loaders:[‘xx’]的形式,如果一律采用concat的形式,显然过于粗暴了。所以,webpack-merge提供了更为聪明的的merge.smart函数,可以将test值相同的loader(preLoaders,postLoaders)进行合并

  • 对于loader,后面的会覆盖前面的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
merge.smart({
loaders: [{
test: /\.js$/,
loader: 'babel'
}]
}, {
loaders: [{
test: /\.js$/,
loader: 'coffee'
}]
});
// 结果为
{
loaders: [{
test: /\.js$/,
loader: 'coffee'
}]
}
  • loaders将会合并为一个去重后的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
merge.smart({
loaders: [{
test: /\.js$/,
loaders: ['babel']
}]
}, {
loaders: [{
test: /\.js$/,
loaders: ['coffee']
}]
});
// 结果为
{
loaders: [{
test: /\.js$/,
// 由于webpack处理loadersz中的值是按照从右往左的顺序,所以'coffee'放在'babel'的前面
// 这种从右往左的方式,让我们可以构造loaders处理链
loaders: ['coffee', 'babel']
}]
}

注意:对于loaders中带参数的形式,如babel?plugins[]=object-assign,在smart merge时会认为和babel、babel?…为同一类,后一个会对前一个产生覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
merge.smart({
loaders: [{
test: /\.js$/,
loaders: ['babel?plugins[]=object-assign']
}]
}, {
loaders: [{
test: /\.js$/,
loaders: ['babel', 'coffee']
}]
});
// 结果为
{
loaders: [{
test: /\.js$/,
loaders: ['babel', 'coffee']
}]
}
  • 如果后一个loaders,前一个为loader,则merge时会将loader转化为loaders,然后进行merge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
merge.smart({
loaders: [{
test: /\.js$/,
loader: 'babel'
}]
}, {
loaders: [{
test: /\.js$/,
loaders: ['coffee']
}]
});
// will become
{
loaders: [{
test: /\.js$/,
// 由于webpack处理loadersz中的值是按照从右往左的顺序,所以'coffee'放在'babel'的前面
loaders: ['coffee', 'babel']
}]
}
  • 如果后一个为loader,前一个为loaders,则merge的结果为loader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
merge.smart({
loaders: [{
test: /\.js$/,
loaders: ['babel']
}]
}, {
loaders: [{
test: /\.js$/,
loader: 'coffee'
}]
});
// will become
{
loaders: [{
test: /\.js$/,
loader: 'coffee'
}]
}

注意:include和exclude的配置同时也会影响loader是否会被合并。如果两个loaders有不同的include或者exclude值(后一个loaders不包含include或exclude除外),那么loaders最终不会进行合并,而是像merge中进行了concat。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
merge.smart({
loaders: [{
test: /\.js$/,
include: '/dir1',
loader: 'babel'
}]
}, {
loaders: [{
test: /\.js$/,
include: '/dir2',
loader: 'coffee'
}]
});
//merge结果为
{
loaders: [{
test: /\.js$/,
include: '/dir2',
loader: 'coffee'
},
{
test: /\.js$/,
include: '/dir1',
loader: 'babel'
}]
}