Рекомендации по настройке Webpack 4

Node.js внешний интерфейс JavaScript Webpack
Рекомендации по настройке Webpack 4

Автор Даниэль Ант Технологическая группа по работе с финансовыми данными

Прошло некоторое время с момента выпуска Webpack 4. Номер версии Webpack стал 4.12.x. Однако из-за того, что официальное руководство по миграции Webpack еще не завершено, а документации по-прежнему не хватает, большинство людей по-прежнему не уверены в обновлении Webpack.

Тем не менее, команда разработчиков Webpack написала несколько разрозненных статей, а также на официальном сайте есть новые документы по настройке. Некоторые разработчики в сообществе также успешно протестировали воду, обновились до Webpack 4 и резюмировали результаты в блоге. Так я наконец-то познакомился со спецификой Webpack 4. Вот мой опыт перехода на Webpack 4.

В центре внимания этой статьи:

  • Какое удобство в настройке Webpack 4? Что нужно изменить в файле конфигурации для миграции?
  • Предыдущие рекомендации по настройке Webpack по-прежнему применимы в этой версии Webpack 4?

Лучшие практики Webpack до Webpack 4

Вот официальный шаблон Webpack Vuevuejs-templates/webpackНапример, давайте поговорим о том, как более зрелые файлы конфигурации Webpack в сообществе были организованы до Webpack 4.

Отдельные среды разработки и производства

Примерная структура каталогов выглядит следующим образом:

+ build
+ config
+ src

В каталоге сборки есть четыре конфигурации веб-пакета. Они есть:

  • webpack.base.conf.js
  • webpack.dev.conf.js
  • webpack.prod.conf.js
  • webpack.test.conf.js

Это соответствует конфигурации среды разработки, производства и тестирования соответственно. Среди них webpack.base.conf.js — некоторые общедоступные элементы конфигурации. Мы используемwebpack-mergeПолучите эти общедоступные элементы конфигурации, и элемент конфигурации Mrge для конкретной среды станет полным элементом конфигурации. Например, в webpack.dev.conf.js:

'use strict'
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')

const devWebpackConfig = merge(baseWebpackConfig, {
   ...
})

Отличаются не только некоторые конфигурации этих трех сред, но, что более важно, каждая конфигурация используетwebpack.DefinePluginвнедряется в кодNODE\_ENVэта переменная окружения.

Эта переменная имеет разные значения в разных средах, таких как РАЗРАБОТКА. Значение этих переменных среды определяется в файле конфигурации в папке Config. WebPack сначала считывает это значение из файла конфигурации и внедряет. Например:

build/webpack.dev.js

plugins: [
  new webpack.DefinePlugin({
    'process.env': require('../config/dev.env.js')
  }),
]

config/dev.env.js

module.exports ={
  NODE_ENV: '"development"'
}

Что касается конкретных значений переменных среды в разных средах, например, среда разработки — это разработка, а среда производства — это производство, что на самом деле является соглашением.

Авторы фреймворков, библиотек или нашего бизнес-кода, будут некоторые коды, которые выносят суждения в соответствии с окружением и выполняют различную логику, например:

if (process.env.NODE_ENV !== 'production') {
  console.warn("error!")
}

Код будет предварительно выполнен один раз, когда код минимизируется, и если условное выражение оценивается как истинное, содержимое истинной ветки удаляется. Это оптимизация мертвого кода во время компиляции. Эта практика выделения разных сред и установки разных значений для переменных среды позволяет нам открыть возможность оптимизации кода в соответствии со средой во время компиляции.

Code Splitting && Long-term caching

Разделение кода обычно требует следующих действий:

  • Упаковано отдельно для Вендора (Вендор относится к сторонним библиотекам или публичным базовым компонентам, т.к. Вендор меняет меньше, упаковка отдельно хороша для кеширования)
  • Пакет отдельно для манифеста (код времени выполнения Webpack)
  • Упаковать публичный бизнес-код для разных входов (аналогично еще и для кеширования и скорости загрузки)
  • Сделать общедоступный пакет для асинхронно загружаемого кода

Разделение кода обычно выполняется путем настройки плагина CommonsChunkPlugin. Типичная конфигурация выглядит следующим образом: CommonsChunkPlugin настроен для поставщика, манифеста и поставщика-асинхронного соответственно.

    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks (module) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

Характерной чертой CommonsChunkPlugin является то, что конфигурацию трудно понять, и конфигурация каждого часто копируется, и эти коды в основном становятся шаблонными. Если требования Code Splitting простые, это лучше, если есть специальные требования, такие как разные пакеты для разных поставщиков входа, настроить будет сложно. В общем, настройка разделения кода — это боль.

И стратегия долгосрочного кэширования такова: дайте статическим файлам длительный срок действия кэша, например, один год. Затем добавьте хэш к имени файла.Каждый раз, когда вы строите, когда содержимое файла изменяется, хэш в имени файла также будет меняться. Браузер использует имя файла в качестве идентификатора файла, поэтому при изменении хэша браузер перезагрузит файл.

Хэш имени файла можно настроить в опции вывода Webpack, например:

output: {
  path: config.build.assetsRoot,
  filename: utils.assetsPath('js/[name].[chunkhash].js'),
  chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},

Лучшие практики в Webpack 4

Изменения и изменения в Webpack 4

В API этой версии Webpack 4 есть некоторые критические изменения, но это не означает, что эта версия претерпела кардинальные изменения. На самом деле изменилось всего несколько пунктов. И пока вы внимательно понимаете эти изменения, вам наверняка будут аплодировать.

Переход на Webpack 4 также просто необходимо проверитьchecklist, чтобы увидеть, покрыты ли эти точки, вот и все.

Различие между средами разработки и производства

Представлен Webpack 4modeэтот вариант. Значением этого варианта может быть разработка или производство.

После установки режимаprocess.env.NODE\_ENVТакже настроен на разработку или производство. Затем в производственном режиме по умолчанию будет включен ряд плагинов, таких как UglifyJsPlugin.

Webpack 4 поддерживает использование без настройки.Вы можете указать местоположение входа из командной строки.Если вы не укажете его, этоsrc/index.js. Параметр режима также может быть передан как параметр командной строки. Таким образом, некоторые часто используемые оптимизации упаковки производственной среды могут быть включены напрямую.

Мы должны отметить, что нулевая конфигурация Webpack 4 ограничена.Если вы хотите добавить плагины, которые вы хотите добавить, или вы хотите добавить несколько записей, вам все равно нужен файл конфигурации.

Тем не менее, Webpack 4 усердно работал на всех фронтах, чтобы сделать возможным больше вещей с нулевой конфигурацией. Этот встроенный метод оптимизации позволяет нам сосредоточиться на развитии бизнеса в начале проекта и уделять внимание подготовке файлов конфигурации только тогда, когда бизнес усложняется позже.

До того, как Webpack 4 представил параметр режима, если мы хотели создать разные параметры сборки для разных сред разработки, мы могли только создать несколько конфигураций Webpack и установить разные значения переменных среды. Это также лучшая практика в сообществе.

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

Итак, метод использования нескольких конфигураций Webpack и ручного внедрения переменных среды, описанный выше, неприменим в Webpack 4? вообще-то нет.В Webpack 4 для серьезного проекта нам все еще нужно несколько разных файлов конфигурации.. Если мы делаем что-то особенное с упаковкой для тестовой среды, нам также нужно использовать в этом файле конфигурацииwebpack.DefinePluginРучной впрыскNODE\_ENVзначение (например, тест).

Если вам нужна тестовая среда под Webpack 4, то режим тестовой среды тоже разработка. Поскольку существует только два режима разработки и производства, тестовая среда должна относиться к стадии разработки.

Выбор сборок сторонних библиотек

В эпоху Webpack 3 нам нужно установить псевдоним для сторонней библиотеки в конфигурации Webpack рабочей среды, а путь этой библиотеки указать путь к файлу рабочей сборки. Таким образом вводится зависимость производственной версии.

Например:

resolve: {
  extensions: [".js", ".vue", ".json"],
  alias: {
    vue$: "vue/dist/vue.runtime.min.js"
  }
},

После введения режима в Webpack 4 для некоторых зависимостей мы не можем настраивать псевдонимы, например React. Входной файл для React выглядит так:

'use strict';

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

Это позволяет конфигурации 0 автоматически выбирать производственную сборку.

Но большинство третьих библиотек не выносят суждений об окружающей среде для этой записи. Так что в этом случае нам все равно нужно вручную настроить псевдоним.

Code Splitting

В Webpack 4 есть еще одно большое изменение, заключающееся в отказе от CommonsChunkPlugin и введенииoptimization.splitChunksэтот вариант.

optimization.splitChunksОн не установлен по умолчанию. Если режим производственный, Webpack 4 включит разделение кода.

По умолчанию Webpack 4 будет разделять только код по требованию. Если нам нужно настроить код для начальной загрузки, чтобы он также добавлялся в разделение кода, мы можем установитьsplitChunks.chunksза'all'.

Самой большой особенностью разделения кода Webpack 4 является его простая конфигурация (начиная с конфигурации 0) и автоматическое разделение на основе встроенных правил. Встроенные правила сегментации кода следующие:

  • На новый пакет ссылаются два или более модулей или из node_modules
  • Новые пакеты размером более 30 КБ (до сжатия)
  • Асинхронная загрузка одновременно загружаемых пакетов не может превышать 5
  • Количество изначально загруженных пакетов не может быть больше 3

Проще говоря, Webpack автоматически извлечет общедоступные модули в коде и превратит их в пакет, при условии, что пакет больше 30 КБ, иначе Webpack не будет извлекать общий код, потому что стоимость увеличения запроса нельзя игнорировать.

В конкретных бизнес-сценариях конкретную логику разделения можно найти вДокументация для плагина SplitChunksа такжеwebpack 4: Code Splitting, chunk graph and the splitChunks optimizationэтот блог. Эти две статьи в основном перечисляют все возможные ситуации.

Для обычных приложений достаточно правил, встроенных в Webpack 4.

Если это особое требование, Webpack 4optimization.splitChunksAPI также может удовлетворить.

У splitChunks есть параметр cacheGroups, аналогичный предыдущему экземпляру CommonChunks. Каждый объект в cacheGroups представляет собой определенный пользователем фрагмент.

Как мы упоминали ранее, Webpack 4 имеет встроенный набор правил разделения кода, и пользователи также могут настраивать cacheGroups, то есть пользовательские фрагменты. К какому чанку должен быть отрисован модуль? Это контролируется областью извлечения cacheGroups. Каждая группа cacheGroups может определять область действия своего собственного модуля извлечения, то есть общий код, в котором файлы будут извлекаться в свой собственный чанк. Если есть пересечение областей модуля между разными cacheGroups, мы можем использовать свойство priority для управления приоритетом. Приоритет извлечения Webpack 4 по умолчанию — самый низкий, поэтому модули сначала будут извлечены в пользовательский фрагмент.

splitChunksPlugin предоставляет два способа управления областью действия модулей извлечения фрагментов. Одним из них является тестовый атрибут. Этот атрибут может быть передан в виде строки, регулярки или функции.Все модули будут соответствовать условиям, переданным тестом.При выполнении условий они будут включены в область действия альтернативных модулей этого чанка. Если условие, которое мы передаем, является строкой или регуляркой, процесс сопоставления выглядит следующим образом: сначала сопоставляется путь модуля, а затем сопоставляется имя чанка, в котором находился модуль.

Например, мы хотим упаковать все модули, представленные в node_modules, в один модуль:

  vendors1: {
    test: /[\\/]node_modules[\\/]/,
    name: 'vendor',
    chunks: 'all',
  }

Поскольку все зависимости, загруженные из node_modules, имеют в своем пути node_modules, это регулярное выражение будет соответствовать всем зависимостям, загруженным из node_modules.

Атрибут test может управлять диапазоном извлечения фрагментов в единицах модулей, что является точным способом. Второй способ, которым splitChunksPlugin управляет областью действия извлеченных модулей, — это свойство chunks. куски могут быть строками, такими как'all'|'async'|'initial', представляющий все фрагменты, фрагменты, загруженные по запросу, и фрагменты, загруженные изначально. чанки также могут быть функцией, в этой функции мы можем получитьchunk.name. Это дает нам возможность разделить наш код по записи. Это мелкозернистый и более крупный подход, разбитый на куски.

Например, допустим, у нас есть три записи a, b, c. Мы хотим, чтобы общий код a и b был упакован отдельно как общий. То есть код c не участвует в разделении общего кода.

Мы можем определить cacheGroups, а затем установить свойство chunks в функцию, которая отвечает за фильтрацию того, какие куски содержатся в этом cacheGroups. Пример кода выглядит следующим образом:

  optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          chunks(chunk) {
            return chunk.name !== 'c';
          },
          name: 'common',
          minChunks: 2,
        },
      },
    },
  },

Приведенная выше конфигурация означает: мы хотим упаковать общий код в записях a и b в блок с именем common. использоватьchunk.name, мы можем легко выполнить это требование.

В приведенном выше случае мы знаем, что свойство chunks можно использовать для разделения групп общего кода по записи. Теперь давайте рассмотрим немного более сложный случай: групповые зависимости в node_modules, представленные в разных записях группировки.

Например, у нас есть четыре записи a, b, c, d. Мы хотим, чтобы зависимости a и b были упакованы как поставщик1, а зависимости c и d были упакованы как поставщик2.

Это требование требует от нас фильтрации как записи, так и модуля, поэтому нам нужно использовать атрибут test, который является более точным способом. Наша идея состоит в том, чтобы написать две группы cacheGroups, и условием оценки одной группы cacheGroup является: если модуль введен в чанк a или b, а путь к модулю содержитnode\_modules, то этот модуль нужно запаковать в vendors1. вендоры2 делают то же самое.

  vendors1: {
    test: module => {
      for (const chunk of module.chunksIterable) {
			if (chunk.name && /(a|b)/.test(chunk.name)) {
				if (module.nameForCondition() && /[\\/]node_modules[\\/]/.test(module.nameForCondition())) {
                 return true;
             }
			}
	   }
      return false;
    },
    minChunks: 2,
    name: 'vendors1',
    chunks: 'all',
  },
  vendors2: {
    test: module => {
      for (const chunk of module.chunksIterable) {
			if (chunk.name && /(c|d)/.test(chunk.name)) {
				if (module.nameForCondition() && /[\\/]node_modules[\\/]/.test(module.nameForCondition())) {
                 return true;
             }
			}
	   }
      return false;
    },
    minChunks: 2,
    name: 'vendors2',
    chunks: 'all',
  },
};

Long-term caching

Долгосрочное кэширование Здесь основная операция такая же, как и в Webpack 3. Однако есть небольшая проблема в работе долгосрочного кэширования Webpack 3. Эта проблема связана с несоответствием между содержимым чанка и изменением хеша:

При неизменном содержимом общего кода Vendor при добавлении записи, или внешней зависимости, или асинхронного модуля хэш Vendor изменится.

Ранее в официальной колонке Webpack была статья об этой проблеме:Predictable long term caching with Webpack. дает решение.

Суть этого решения заключается в том, что Webpack внутренне поддерживает автоматически увеличивающийся идентификатор, и каждый фрагмент имеет идентификатор. Таким образом, когда добавляется запись или другие типы фрагментов, идентификатор изменяется, что приводит к изменению идентификатора фрагмента, содержимое которого не изменилось.

Наш ответ на это заключается в использованииwebpack.NamedChunksPluginПревратите идентификатор фрагмента в строковый идентификатор, который обычно представляет собой относительный путь к модулю. Таким образом можно стабилизировать идентификатор фрагмента модуля.

Screen Shot 2018-06-03 at 12.59.28 AM.png | left

Здесь vendors1 — это идентификатор чанка.

HashedModuleIdsPluginФункция аналогична NamedChunksPlugin, за исключением того, что HashedModuleIdsPlugin использует хэш, сгенерированный в соответствии с относительным путем модуля, в качестве идентификатора чанка, поэтому идентификатор чанка будет короче. Поэтому HashedModuleIdsPlugin больше рекомендуется в продакшене.

В статье также говорится,webpack.NamedChunksPluginРаботает только с обычными модулями Webpack, асинхронными модулями, внешние модули не работают.

Асинхронные модули могут быть аннотированы с помощью chunkName при импорте, например: import(/* webpackChunkName: "lodash" */ 'lodash').then()

Поэтому нам нужно использовать еще один плагин:name-all-modules-plugin

Этот плагин использует какой-то старый API, Webpack 4 выдаст предупреждение, этоprЕсть новые версии, но автор не обязательно сольет. Когда мы его используем, мы можем напрямую скопировать код этого плагина в нашу конфигурацию Webpack.

После этого ChunkId нашего поставщика никогда не изменится так, как не должен.

Суммировать

Изменения в Webpack 4 в основном представляют собой ассимиляцию лучших практик сообщества. Webpack 4 значительно улучшает возможности разделения кода с помощью нового API. Однако проблема хеша поставщика в долгосрочном кэшировании до сих пор не решена и требует ручной настройки. В этой статье в основном представлены сходства и различия передовых методов настройки Webpack в контексте Webpack 3.x и 4.x. Надеюсь, это поможет с организацией файлов конфигурации для проектов читателей Webpack 4.

Кроме того, рекомендуетсяSURVIVEJS - WEBPACKэтот онлайн-учебник. В этом руководстве обобщается практика использования Webpack в реальной разработке и обновляются материалы до последней версии Webpack 4.

Если вам интересна наша команда, вы можете подписаться на рубрику и подписатьсяgithubИли отправьте свое резюме на 'tao.qit####alibaba-inc.com'.replace('####', '@'), люди с высокими идеалами могут присоединиться~

Оригинальный адрес:GitHub.com/proto team/no…