Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack-chain 从入门到深入 #29

Open
fengshi123 opened this issue Apr 6, 2021 · 0 comments
Open

webpack-chain 从入门到深入 #29

fengshi123 opened this issue Apr 6, 2021 · 0 comments

Comments

@fengshi123
Copy link
Owner

一、前言

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 ~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant