На днях вышла новая версия популярной системы сборки Webpack 4.0. Команда опубликовала большой Changelog и восторженный пост на Medium с описанием обновлений системы.
К текущему моменту не все изменения описаны в официальной документации. В этой заметке — обзор новых возможностей и проблем, с которыми вы можете столкнуться при обновлении.
Режимы сборки
Добавлен обязательный параметр --mode, определяющий один их трех режимов сборки: production, development и none, от которых зависят различные оптимизации и внешний вид бандла. Режим production оптимизирован под уменьшение размера сборки, а development — уменьшение времени сборки. В режиме none все оптимизации отключены.
А еще можно сконфигурировать собственный режим, отключив или включив определенные оптимизации с помощью опций optimization.* в конфиге. Доступные настройки отсутствуют в документации, зато их описал основатель Webpack Тобиас Копперс в блоге на Medium.
import() CommonJS-модулей
Больше нельзя подключать CommonJS-модули через import(). То есть либо используйте require, либо пишите модули по стандарту ES6 modules.
CommonJS
const a = require("./a")
module.exports = { a, b: 2 }
ESM
import a from "./a"
export default { a, b: 2 }
Удаленные плагины
- NoEmitOnErrorsPlugin или NoErrorsPlugin (не создает сборку при ошибках). Доступен из коробки как optimization.noEmitOnErrors. Включен по умолчанию в production mode
- ModuleConcatenationPlugin (объединяет несколько модулей в одном замыкании, чем уменьшает бандл и ускоряет выполнение). Теперь это делается из коробки в production mode, либо включается настройкой optimization.concatenateModules
- NamedModulesPlugin (улучшает внешний вид имени модуля при Hot Module Replacement, который обновляет только изменившиеся части сборки). Теперь это опция optimization.namedModules, которая включена по умолчанию в режиме development
- NewWatchingPlugin (улучшает пересборку при изменениях) — больше не требуется
- CommonsChunkPlugin (выделяет общие части нескольких точек входа в отдельную сборку) — заменен на набор API `optimize.splitChunks`. Авторы даже написали пост, сделали ролик и опубликовали gist на эту тему.
Минификация
Отдельным пунктом про UglifyJsPlugin. Он, наконец, научился поддерживать ES6 модули, но в базовой конфигурации он больше не нужен — для минификации сборки используется опция optimization.minimize = true, которая по умолчанию включена в режиме production. Не используемый код (dead code) Webpack теперь тоже вырезает без помощи UglifyJs.
Плагин или функцию, которая будет заниматься минификацией, можно переопределить в optimization.minimizer.
Типы модулей
Изначально Webpack был ориентирован именно на сборку JavaScript-файлов. Но впоследствии его стали применять для сборки всего на свете: CSS, HTML, картинок и тд, в процессе испытывая боль с конфигурацией точек входа, лоадерами и плагинами. Теперь появились Module Types:
- javascript/auto — JavaScript-модуль в формате CommonJS, AMD или ESM
- javascript/esm — строго ES-модуль. По умолчанию применяется для .mjs файлов.
- javascript/dynamic — либо CommonJs, либо AMD
- json — JSON-файл, которые теперь можно подключать напрямую через require или import. По умолчанию включен для .json файлов
- webassembly/experimental — модуль WebAssembly, по умолчанию для .wasm файлов
Типы модулей определяются либо автоматически по расширению файла, либо задаются с помощью свойства module.rules[].type в конфиге.
module: {
rules: [
{
test: /\.js$/,
type: 'javascript/esm',
use: {
loader: 'eslint-loader',
}
},
]
}
По словам разработчиков, введение типов позволит им в следующих обновлениях Webpack добавить возможность создавать CSS, HTML и другие сборки, без точки входа на JavaScript.
Webpack-cli вынесен в отдельный проект
Это значит, что чтобы запускать сборку из командной строки или через менеджер пакетов, нужно установить webpack-cli вручную.
npm install --save-dev webpack-cli
или
yarn add --dev webpack-cli
Поддержка WebAssembly
Появилась возможность подключать WebAssembly-модули через import или require. Другими словами, в сборку можно подключать бинарные скрипты C++, C, Rust и другие, но для этого потребуются соответствующие лоадеры.
Сами модули WebAssembly тоже могут «реквайрить» другие .js и .wasm модули.
Поддержка sideEffects: false в package.json
Свойство sideEffects в файле package.json в значении false сообщает, что пакет не содержит действий, изменяющих состояние внешних переменных, DOM-узлов и тд.
Он упрощает встроенный Tree Shaking и оптимизирует сборку переиспользуемых частей кода.
sideEffects можно переопределить для каждого модуля в свойстве module.rules
Магические комментарии к динамическим импортам
В Webpack конструкции вида
modulesList.forEach( module => require('src/' + module) );
по умолчанию не поддерживаются, так как система на этапе сборки не знает значение переменных.
Возможность создавать динамические импорты реализуется с помощью ContextReplacementPlugin, который заранее включает в сборку файлы по заданным паттернам. Еще один вариант динамических импортов — асинхронная подгрузка модулей через Code Splitting.
Суть обновления в том, что теперь вы можете фильтровать файлы в динамических импортах с помощью магических комментариев webpackInclude и webpackExclude. В документации описания этой возможности нет, но делается примерно так:
import(
/* webpackExclude: "system.js" */
'module'
);
Больше информации при сборке
- Добавлена валидиция настроек плагинов. Поддерживаются не все, но какие — не уточняется.
- ProgressPlugin, включаемый опцией --progress, теперь выводит названия плагинов рядом с процентами прогресса сборки
- Опция Stats теперь выводит названия вложенных модулей
- Некорректное значение options.dependencies приводит к ошибке сборки
- Больше информации при ошибках подгрузки chunk-модулей.
- Более понятное сообщение о том, что какой-то плагин был удален
Работа с переменными окружения
Если раньше вы передавали чувствительные данные, хранящиеся в переменных окружения с помощью DefinePlugin, то теперь это можно сделать «из коробки» с помощью optimization.nodeEnv: true, тогда они автоматически попадут в proccess.env.NODE_ENV.*
Этой возможности нет в Changelog, но она есть в исходниках. И, видимо, в определенный момент появится в документации.
Source Maps
Опция devtools теперь поддерживает свойства include, test и exclude.
В режиме development по умолчанию генерируются source maps типа eval. Для более детальной настройки source maps разработчики рекомендуют использовать SourceMapDevToolPlugin.
Дефолтная конфигурация
Для простейшей сборки больше не требуется webpack.config.js, тк ряд обязательных параметров получил значения по умолчанию.
- Точка входа по умолчанию — ./src/index.js
- Output-директория, в которой окажется сборка — ./dist
- Режим сборки по умолчанию — production
Если вы занимаетесь разработкой лоадеров или плагинов для Webpack, обратите внимание на список изменений в API без обратной совместимости.
Troubleshooting
Если вы решили обновить версию Webpack, то начать лучше с удаления всех модулей и плагинов (а лучше — всей локальной папки node_modules), после чего установить новый Webpack и затем — поставить плагины заново. Это сэкономит время на решении проблем с совместимостью и обновлением зависимостей.
Дальше разбор некоторых частых ошибок.
The CLI moved into a separate package: webpack-cli.
Please install 'webpack-cli' in addition to webpack itself to use the CLI.
-> When using npm: npm install webpack-cli -D
-> When using yarn: yarn add webpack-cli -D
— установить webpack-cli
Error: webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead.
— отключить плагин, заменить на optimization.minimize: true
WARNING in configuration
The 'mode' option has not been set. Set 'mode' option to 'development' or 'production' to enable defaults for this environment.
— передать --mode production (или development) при запуске скрипта webpack
npm WARN [email protected] requires a peer of webpack@^2.2.0 but none was installed.
— избавиться от ExtractTextWebpackPlugin (разработчики за полтора месяца тестирования беты Webpack 4 не предоставили обновления), либо, потратив N-времени в гугле, найти и установить нестабильную альфу.
npm i --save-dev [email protected]
Зачем это все
Шон Ларкин, технарь Microsoft Edge и один из ведущих разработчиков Webpack и Angular Cli, провел опрос в твиттере с предложением сравнить скорость сборки до и после обновления на 4 версию. По его словам, прирост составил до 98%
Что дальше
А дальше последуют патч-версии (читай, багфиксы), одна из которых вышла пока писался этот материал. Существенный прирост в скорости сборки во многом был обусловлен рефакторингом ядра, который позволит реализовать старые featue-реквесты и свежие идеи на обновленном API. Шон уже озвучил ряд нововведений, которые появятся либо в минорных обновлениях четвертой версии, либо, вероятнее всего, в Webpack 5.
- ESM Module Target
- Persistant-кэширование (видимо, не будет сбрасываться при остановке watch)
- Поддержка WebAssembly-модулей станет stable, добавление Tree Shaking
- Расширение 0CJS — то есть сборки без конфигурации. По сути — расширение пула дефолтных значений.
- «Многопоточная» сборка.
- CSS Module Type. Можно будет создавать отдельные CSS сборки со своей точкой входа. Шон попрощался с ExtractTextWebpackPlugin (и со всеми контрибьютерами этой разработки)
- То же самое с HTML Module Type
- URL/File Module Type. То есть в качестве точки входа скоро сможет выступить даже изображение.
- [Create-Your-Own] Module Type. Вот что назвается, рефакторинг.
Шон завершает публикацию словами о Ренессансе JavaScript и «переосмыслении» миссии Webpack.
А мы продолжаем работать.