Я был недоволен, чтобы настроить WebPack, пока не столкнулся с цепочкой WebPack.

внешний интерфейс JavaScript

Сегодня я хотел бы представить сообществу схему настройки потоковой передачи webpack - webpack-chain.Эта схема сейчас реализована в моей текущей команде и принесла некоторые положительные результаты.Теперь давайте взглянем на предысторию и суть Эта схема.Концепция и поза для ежедневного использования будут представлены всем.

Почему появляется webpack-chain?

Я считаю, что все знакомы с известными в отрасли инструментами сборки.WebpackЭто не новичок.Как самый стабильный и наиболее широко используемый инструмент сборки и упаковки в производственной среде, он, безусловно, имеет много преимуществ, таких как:

  • Экологически богатый. В сообществе много загрузчиков и плагинов, и в принципе можно найти все, что угодно.
  • Подключаемый механизм плагинов. Масштабируемая архитектура, основанная на реализации Tapable.
  • Документация зрелая. Есть китайская версия, и она обновлялась и поддерживалась.
  • Высокая стабильность. Сейчас официальная производственная среда фронтенд-проекта в основном построена с помощью Webpack.После стольких лет проверки в отрасли ямы, на которые следует наступать, почти те же.

Но на самом деле, после разговора о многих преимуществах, по оценкам, все все еще не понравится, потому что есть еще самый важный момент, который нельзя игнорировать, то есть опыт развития. Для строительства и упаковки это изначально один из инженеровДетали очень сложныечасть, нужно输入大量的配置信息чтобы убедиться, что результаты упаковки соответствуют ожиданиям. существуетWebpackСреди них, если мы не используем другие решения, мы можем только вручную настроить огромный объект JavaScript, и вся информация о конфигурации находится в этом объекте, поэтому исходный метод действительно плох для людей, что можно резюмировать следующим образом. причины:

  1. Объект слишком велик и визуально ослепляет.Хотя некоторая логика может быть инкапсулирована, глубокой вложенности конфигурации не избежать;

  2. Сложность динамического изменения. Например, если вы динамически изменяете некоторую информацию о конфигурации с помощью скриптов, например, удаляете подключаемый модуль babel-loader, вам необходимо найти расположение babel-loader шаг за шагом в объекте конфигурации верхнего уровня, а затем пройтись по списку плагины Этот ручной поиск и процесс обхода довольно громоздкий.

  3. Трудно поделиться конфигурацией. Если вы попытаетесь поделиться объектами конфигурации webpack между проектами, последующие изменения будут беспорядочными, потому что вам нужно динамически изменять исходную конфигурацию.

Некоторые люди в сообществе также обнаружили эти болевые точки, поэтомуWebpackСхема настройки потоковой передачи -webpack-chain.

Основные концепции цепочки webpack

на самом деле учитьсяwebpack-chain, я думаю в первую очередь не изучать способ настройки каждого атрибута, а пониматьwebpack-chainОсновные два объекта -ChainedMapа такжеChainedSet.

Что такое цепная карта?

Например, сейчас я настраиваю псевдоним пути:

config.resolve.alias
  .set(key, value)
  .set(key, value)
  .delete(key)
  .clear()

Затем текущий объект alis представляет собойChainMap. Если атрибут находится вwebpack-chainкоторый отмечен какChainMapПосле этого у него появятся дополнительные методы и будут разрешены эти цепочки вызовов (как в примере выше).

Далее, давайте познакомимся с этими методами один за другим:

// 清空当前 Map 的所有属性
clear()
// 通过键值从 Map 移除单个配置.
delete(key)
// Map中是否存在一个配置值的特定键,返回真或假
has(key)
// 返回 Map中已存储的所有值的数组
values()
//  提供一个对象,这个对象的属性和值将映射进 Map。第二个参数为一个数组,表示忽略哪些属性
merge(obj, omit)
// handler: ChainedMap => ChainedMap
// 一个把ChainedMap实例作为单个参数的函数
batch(handler)
// condition: Boolean
// whenTruthy: ChainMap -> any, 条件为真时执行
// whenFalsy: ChainSet -> any, 条件为假时执行
when(condition, whenTruthy, whenFalsy)
// 获取 Map 中相应键的值
get(key)
// 先调用 get,如果找不到对应的值, 就返回 fn 函数返回的结果
getOrCompute(key, fn)
// 配置键值对
set(key, value)

Возвращаемыми объектами этих методов также являются ChainMaps, которые могут реализовывать цепные вызовы и упрощать операции. существуетWebpack, большинство объектов - это ChainMap, вы можете перейти кисходный кодСмотрите, реализация не сложная.

Цепная картаwebpack-chainОчень важная структура данных инкапсулирует метод цепного вызова, так что все конфигурации типа ChainMap можно повторно использовать напрямую.ChainMapЭти методы сами по себе очень удобны.

Что такое цепной набор?

Подобно ChainMap, он инкапсулирует собственный набор API:

// 末尾增加一个值
add(value)
// 在开始位置增加一个值
prepend(value)
// 清空 set 内容
clear()
// 删除某个值
delete(value)
// 判断是否有某个值
has(value)
// 返回值列表
values()
// 合并给定的数组到 Set 尾部。
merge(arr)
// handler: ChainSet => ChainSet
// 一个把 ChainSet 实例作为单个参数的函数
batch(handler)
// condition: Boolean
// whenTruthy: ChainSet -> any, 条件为真时执行
// whenFalsy: ChainSet -> any, 条件为假时执行
when(condition, whenTruthy, whenFalsy)

ChainSetроль иChainMapТочно так же это также API, который инкапсулирует базовые связанные вызовы.WebpackПри настройке свойств типа массива вызовомChainSetМетод может быть завершен.

сокращенный метод

дляChainMap, есть такой упрощенный способ написания, на официальном сайте он называетсястенография:

devServer.hot(true);

// 上述方法等效于:
devServer.set('hot', true);

Поэтому в действительностиwebpack-chainВ комплектации часто можно увидеть прямой.属性()Такой способ призыва, не кажется ли он очень умным? Реализация в исходном коде очень проста:

extend(methods) {
  this.shorthands = methods;
  methods.forEach(method => {
    this[method] = value => this.set(method, value);
  });
  return this;
}

существуетChainMapПри инициализации вызывается метод extend, а затем список свойств используется какmethodsПараметры передаются напрямую, а затем метод set вызывается косвенно через следующую строку кода:

this[method] = value => this.set(method, value);

Этот дизайн также стоит изучить.

Настроить веб-пакет

Во-первых, необходимо создать новый объект конфигурации:

const Config = require('webpack-chain');

const config = new Config();

// 一系列链式操作之后
// 得到最后的 webpack 对象
console.log(config.toConfig())

Затем настройтеresolve,entry,output,module,plugins,optimizationОбъект, ключ этой статьи — познакомить вас с цепочкой веб-пакетов, поэтому я подробно расскажу об использовании каждой конфигурации.

вход и выход

Вот общая конфигурация, потому что Webpack находится вentryа такжеoutputСлишком много свойств, см.Официальная документация WebpackПросто сопоставьте это следующим образом.

config.entryPoints.clear() // 会把默认的入口清空
config.entry('entry1').add('./src/index1.tsx')//新增入口
config.entry('entry2').add('./src/index2.tsx')//新增入口

config.output
      .path("dist")
      .filename("[name].[chunkhash].js")
      .chunkFilename("chunks/[name].[chunkhash].js")
      .libraryTarget("umd")

alias

Настройка псевдонимов путей также является неотъемлемой частью почти всех проектов.Методы настройки следующие:

// 可以发现 resolve.alias 其实是一个 ChainMap 对象
config.resolve.alias
  .set('assets',resolve('src/assets'))
  .set('components',resolve('src/components'))
  .set('static',resolve('src/static'))
  .delete('static') // 删掉指定的别名

plugins

Настройка плагинов можно сказать очень важная часть.Настройка в webpack-chain будет немного отличаться от обычной конфигурации.Давайте рассмотрим ее подробнее.

1. Добавьте плагин

// 先指定名字(这个名字是自定义的),然后通过 use 添加插件
config
  .plugin(name)
  .use(WebpackPlugin, args)

Например:

const ExtractTextPlugin = require('extract-text-webpack-plugin');

// 先指定名字(这个名字可以自定义),然后通过 use 添加插件,use 的第二个参数为插件参数,必须是一个数组,也可以不传
config.plugin('extract')
  .use(ExtractTextPlugin, [{
    filename: 'build.min.css',
    allChunks: true,
  }])

2. Удалите плагин

Удалить плагин легко, помните, при добавлении плагинов мы указывали имя каждого плагина? Теперь удалите по этому имени:

config.plugins.delete('extract')

3. Укажите плагин, который будет вызываться до/после плагина xx.

Например, теперь мне нужно указать, что подключаемый модуль html-webpack-plugin выполняется перед подключаемым модулем извлечения, который я только что написал, поэтому просто напишите:

const htmlWebpackPlugin = require('html-webpack-plugin');

config.plugin('html')
  .use(htmlWebpackPlugin)
  .before('extract')

Через метод before вы можете передать имя другого плагина, что означает, что он будет выполнен перед другим плагином.

Аналогично, если вам нужно выполнить после плагина извлечения, вызовите метод after:

config.plugin('html')
  .use(htmlWebpackPlugin)
  .after('extract')

4. Динамически изменять параметры плагина

Мы также можем использовать webpack-chain для динамического изменения параметров плагина, например:

// 使用 tap 方法修改参数
config
  .plugin(name)
  .tap(args => newArgs)

5. Измените процесс инициализации плагина.

Мы также можем настроить процесс создания экземпляра плагина, например:

// 通过 init 方法,返回一个实例,这将代替原有的实例化过程
config
  .plugin(name)
  .init((Plugin, args) => new Plugin(...args));

loader

loaderЭто незаменимая конфигурация в Webpack.Давайте посмотрим на связанные операции загрузчика.

1. Добавляем загрузчик

config.module
  .rule(name)
    .use(name)
      .loader(loader)
      .options(options)

Например:

config.module
  .rule('ts')
  .test(/\.tsx?/)
  .use('ts-loader')
    .loader('ts-loader')
    .options({
      transpileOnly: true
    })
    .end()

2. Измените параметры загрузчика

Параметры загрузчика можно изменить методом tap:

config.module
  .rule('ts')
  .test(/\.tsx?/)
  .use('ts-loader')
    .loader('ts-loader')
    .tap(option => {
      // 一系列
      return options;
    })
    .end()

После завершения всех настроек вы можете позвонитьconfig.toConfig()Чтобы получить окончательный объект конфигурации, вы можете напрямую использовать его какwebpackКонфигурация.

3. Удалить загрузчик

// 通过 uses 对象的 delete 方法,根据 loader 的 name 删除
config.module
  .rule('ts')
  .test(/\.tsx?/)
  .uses.delete('ts-loader')

optimization

в вебпакеoptimizationЭто также относительно большой объект, см. официальную документацию:Веб-пакет Просто .org/config как у ATI...

здесь, в которомsplitChunksа такжеminimizerНапример, чтобы настроить его:

config.optimization.splitChunks({
     chunks: "async",
     minChunks: 1, // 最小 chunk ,默认1
     maxAsyncRequests: 5, // 最大异步请求数, 默认5
     maxInitialRequests : 3, // 最大初始化请求数,默认3
     cacheGroups:{ // 这里开始设置缓存的 chunks
         priority: 0, // 缓存组优先级
         vendor: { // key 为entry中定义的 入口名称
             chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是async)
             test: /react|vue/, // 正则规则验证,如果符合就提取 chunk
             name: "vendor", // 要缓存的 分隔出来的 chunk 名称
             minSize: 30000,
             minChunks: 1,
         }
     }
});

// 添加一个 minimizer
config.optimization
  .minimizer('css')
  .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: {} }])
// 移除 minimizer
config.optimization.minimizers.delete('css')
// 修改 minimizer 插件参数
config.optimization
  .minimizer('css')
  .tap(args => [...args, { cssProcessorOptions: { safe: false } }])

Эффективно используйте условную конфигурацию

Как упоминалось ранее, дляChainSetа такжеChainMapОбъекты имеют условные методы настройкиwhen, который может заменить if-else в некоторых и многих сценариях, сохранить связанный вызов конфигурации и сделать код более элегантным.

config.when(
  process.env.NODE === 'production',
  config.plugin('size').use(SizeLimitPlugin)
)

резюме

webpack-chainВ качестве схемы потоковой конфигурации веб-пакета объект конфигурации управляется через цепные вызовы, заменяя, таким образом, предыдущий метод ручного управления объектами JavaScript.Облегчая повторное использование конфигурации, он также делает код более элегантным, будь то с точки зрения качества кода или Опыт разработки является хорошим улучшением по сравнению с предыдущим, и всем рекомендуется начать работу.

Эта статья была впервые опубликована в публичном аккаунте «Front-end Sanyuan Classmates», и все желающие могут обратить на это внимание.Оригинальный текст:Мне было трудно настроить Webpack, пока я не наткнулся на эту конфигурацию потоковой передачи.

Команде архитектуры внешнего интерфейса ByteDance IES срочно нужны таланты (p5/p6/p7 много HC), приглашаю добавить меня WeChat sanyuan0704 для общения и приглашаю всех к совместной работе.