webpack-chain 从入门到深入
fengshi123 opened this issue · comments
一、前言
webpack 的核心配置的创建和修改基于一个有潜在难于处理的 JavaScript 对象。虽然这对于配置单个项目来说是没有什么问题的,但当你团队中有比较多项目,并且尝试所有项目共享 webpack 配置文件时,你会觉难以入手,因为你需要考虑构建配置的可扩展性,比如某个子项目有自己独有的特征,需要进行一些个性化配置时,便会变得棘手。
webpack-chain 尝试通过提供可链式或顺流式的 API 创建和修改webpack 配置,API 的 Key 部分可以由用户指定的名称引用,这有助于跨项目修改配置方式的标准化。在 vue-cli3 以及一些开源的构建器中陆续采用了 webpack-chain 这种方式,所以本文我们会从入门到熟练上手,帮助大家熟悉 webpack-chain 的编写使用。
二、语法介绍
1、webpack 实例创建
我们可以使用 npm 或者 yarn 的方式安装 webpack-chain 包,如下所示
npm i --save-dev webpack-chain
or
yarn add --dev webpack-chain
当你安装了webpack-chain, 你就可以开始创建一个 webpack 实例,如下所示
// 导入 webpack-chain 模块,该模块导出了一个用于创建一个 webpack 配置 API 的单一构造函数。
const Config = require('webpack-chain');
// 对该单一构造函数创建一个新的配置实例
const config = new Config();
// ... 中间一系列 webpack 的配置,我们在后续的章节再陆续说明,这里暂且省略
// 导出这个修改完成的要被 webpack 使用的配置对象
module.exports = config.toConfig();
2、ChainedMap
webpack-chain 中的核心 API 接口之一是 ChainedMap. 一个 ChainedMap 的操作类似于 JavaScript Map, 为链式和生成配置提供了一些便利, 如果一个属性被标记一个 ChainedMap, 则它将具有如下的 API 和方法:
除非另有说明,否则这些方法将返回 ChainedMap, 允许链式调用这些方法。
// 1、从 Map 移除所有 配置
clear()
// 2、通过键值从 Map 移除单个配置
delete(key)
// 3、获取 Map 中相应键的值
// 注意:返回值是该 key 对应的值
get(key)
// 4、获取 Map 中相应键的值
// 如果键在 Map 中不存在,则 ChainedMap 中该键的值会被配置为 fn 的返回值.
// 注意:返回值是该 key 对应的值,或者 fn 返回的值
getOrCompute(key, fn)
// 5、配置 Map 中 已存在的键的值
set(key, value)
// 6、Map 中是否存在一个配置值的特定键,
// 注意:返回 boolean
has(key)
// 7、返回 Map 中已存储的所有值的数组
// 注意:返回 Array
values()
// 8、返回 Map 中全部配置的一个对象, 其中 键是这个对象属性,值是相应键的值,
entries()
// 9、 提供一个对象,这个对象的属性和值将 映射进 Map
merge(obj, omit)
// 10、对当前配置上下文执行函数 handler
batch(handler)
// 11、条件执行一个函数去继续配置
// condition: Boolean
// whenTruthy: 当条件为真,调用把 ChainedMap 实例作为单一参数传入的函数
// whenFalsy: 当条件为假,调用把 ChainedMap 实例作为单一参数传入的函数
when(condition, whenTruthy, whenFalsy)
3、ChainedSet
webpack-chain 中的核心 API 接口另一个是 ChainedSet,其操作类似于JavaScript Set, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedSet,则它将具有如下的 API 和方法:
除非另有说明,否则这些方法将返回 ChainedSet,允许链式调用这些方法。
// 1、添加/追加给 Set 末尾位置一个值
add(value)
// 2、添加给 Set 开始位置一个值
prepend(value)
// 3、移除Set中全部值
clear()
// 4、移除Set中一个指定的值
delete(value)
// 5、检测 Set 中是否存在一个值
// 注意:返回 boolean
has(value)
// 6、返回 Set 中值的数组.
// 注意:返回 Array
values()
// 7、连接给定的数组到 Set 尾部。
merge(arr)
// 8、对当前配置上下文执行函数 handler
batch(handler)
// 8、条件执行一个函数去继续配置
// whenTruthy: 当条件为真,调用把 ChainedSet 实例作为单一参数传入的函数
// whenFalsy: 当条件为假,调用把 ChainedSet 实例作为单一参数传入的函数
when(condition, whenTruthy, whenFalsy)
4、方法简写
除了以上提到的使用 ChainedMap 和 ChainedSet 语法编写实现功能外,webpack-chain 还提供了许多简写方法,我们在这里不在一一列出,读者可以去 webpack-chain github 官方文档 查阅。例如,devServer.hot 就是是一个简写方法,写法如下所示
// devServer 的简写方法如下
devServer.hot(true);
// 上述方法等效于
devServer.set('hot', true);
跟 ChainedMap 和 ChainedSet 语法一样,简写方法在没有特别说明的情况,返回的也是原实例,因此简写方法也是支持链式语法的。
5、合并配置
webpack-chain 支持将对象合并到配置实例,但是要注意,这不是 webpack 配置对象,如果我们需要合并 webpack-chain 对象,需要在合并前对其进行转换。
// 合并
config.merge({ devtool: 'source-map' });
// 获取 "source-map"
config.get('devtool')
6、检查生成的配置
我们可以使用语法 config.toString() 方法将 webpack 对象转换成字符串,转换后的字符串包含命名规则、用法和插件的注释提示,如下所示
config
.module
.rule('compile')
.test(/\.js$/)
.use('babel')
.loader('babel-loader');
config.toString();
// 转换后的输出
{
module: {
rules: [
/* config.module.rule('compile') */
{
test: /\.js$/,
use: [
/* config.module.rule('compile').use('babel') */
{
loader: 'babel-loader'
}
]
}
]
}
}
三、常用实例编写
1、entry 入口配置
// 配置编译入口文件
config.entry('main').add('./src/main.js')
// 等同于以下 webpack 配置
entry: {
main: [
'./src/main.js'
]
}
2、output 出口配置
// 配置出口文件
config.output
.path(path.resolve(__dirname, './dist'))
.filename('[name].[chunkhash].js')
.chunkFilename('chunks/[name].[chunkhash].js')
.libraryTarget('umd');
// 等同于以下 webpack 配置
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].[chunkhash].js',
chunkFilename: 'chunks/[name].[chunkhash].js',
libraryTarget: 'umd'
},
3、alias 别名配置
// 配置目录别名
config.resolve.alias
.set('@', path.resolve(__dirname, 'src'))
.set('assets', path.resolve(__dirname, 'src/assets'))
// 等同于以下 webpack 配置
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
assets: path.resolve(__dirname, 'src/assets'))
}
},
4、loader 配置新增
// 配置一个新的 loader
config.module
.rule('babel')
.test(/\.(js|jsx|mjs|ts|tsx)$/)
.include
.add(path.resolve(__dirname, 'src'))
.end()
.use('babel-loader')
.loader('babel-loader')
.options({
'presets':['@babel/preset-env']
})
// 等同于以下 webpack 配置
module: {
rules: [
{
test: /\.(js|jsx|mjs|ts|tsx)$/,
include: [
path.resolve(__dirname, 'src')
],
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
}
}
]
}
]
}
5、loader 配置修改
跟新增 loader 不同的是,使用了 tap 方法,该方法的回调参数为 options 即该 loader 的配置选项对象,从而我们可以通过更改 options 对象,从而去更改 loader 配置。
config.module
.rule('babel')
.use('babel-loader')
.tap(options => {
// 修改它的选项...
options.include = path.resolve(__dirname, 'test')
return options
})
6、loader 配置移除
config.module.rules.clear(); // 添加的 loader 都删掉.
config.module.rule('babel').uses.clear(); 删除指定 rule 用 use 添加的
7、plugin 配置新增
// 配置一个新的 plugin
config.plugin('HtmlWebpackPlugin').use(HtmlWebpackPlugin, [
{
template: path.resolve(__dirname, './src/index.html'),
minify: {
collapseWhitespace: true,
minifyJS: true,
minifyCSS: true,
removeComments: true,
removeEmptyAttributes: true,
removeRedundantAttributes: true,
useShortDoctype: true
}
}
]);
// 等同于以下 webpack 配置
plugins: [
new HtmlWebpackPlugin(
{
template: path.resolve(__dirname, './src/index.html'),
minify: {
collapseWhitespace: true,
minifyJS: true,
minifyCSS: true,
removeComments: true,
removeEmptyAttributes: true,
removeRedundantAttributes: true,
useShortDoctype: true
}
}
)
],
8、plugin 配置修改
跟新增 loader/plugin 不同的是,使用了 tap 方法,且保留了之前配置的选项,更改的选项被覆盖。
// 修改插件 HtmlWebpackPlugin
config.plugin('HtmlWebpackPlugin').tap((args) => [
{
...(args[0] || {}),
template: path.resolve(__dirname, './main.html'),
}
]);
9、使用 when 条件进行配置
// 1、示例:仅在生产期间添加minify插件
config
.when(process.env.NODE_ENV === 'production', config => {
config
.plugin('minify')
.use(BabiliWebpackPlugin);
});
// 2、示例:只有在生产过程中添加缩小插件,否则设置 devtool 到源映射
config
.when(process.env.NODE_ENV === 'production',
config => config.plugin('minify').use(BabiliWebpackPlugin),
config => config.devtool('source-map')
);
10、插件移除配置
config.plugins.delete('HtmlWebpackPlugin');
四、总结
本文我们会从入门到熟练上手,通过介绍 webpack-chain 的语法到手动编写 webpack 的常见配置和操作,帮助大家熟悉 webpack-chain 的编写使用,希望对你有帮助。
辛苦整理良久,还望手动点赞鼓励~ 博客 github地址为:github.com/fengshi123//blog ,汇总了作者的所有博客,欢迎关注及 star ~