Автор Даниэль Ант Технологическая группа по работе с финансовыми данными
Прошло некоторое время с момента выпуска 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.splitChunks
API также может удовлетворить.
У 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
Превратите идентификатор фрагмента в строковый идентификатор, который обычно представляет собой относительный путь к модулю. Таким образом можно стабилизировать идентификатор фрагмента модуля.
Здесь 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…