Настройка webpack 5 [bonus] React Hot Reloading
Базовая настройка webpack 5 + настройка для разработки на React (вкл. React Hot Reloading).
Что, зачем и почему?
Что? Webpack - сборщик модулей для JavaScript. Является одним из мощнейших инструментов современной веб-разработки.
Зачем? Webpack позволяет комфортно создавать приложения по модульной структуре, он собирает все модули в один бандл и минифицирует его. Но это лишь поверхностное описание этого инструмента, на деле вебпак имеет значительно больше возможностей.
Почему? Вам теперь больше нет необходимости беспокоится о сборке проекта, один раз настроил webpack и он все будет делать за вас!
P.S. Ну, или не один раз.
P.S.S. Ладно, точно не один раз.
Установка webpack
Для начала инициализируем наш проект:
npm init -y
После инициализации установим два пакета -
webpack
иwebpack-cli
:
npm i -D webpack webpack-cli
В корне проекта создаем:
src
- папку для файлов приложенияwebpack.config.js
- конфигурационный файл webpack'а.
Начинаем настройку
В папке src
создадим файл index.js
и напишем в нем любой код в ES6+ синтаксисе, например:
const sayHello = () => console.log('hello');
sayHello();
Переходим в файл webpack.config.js
Из этого файла мы экспортируем объект, содержащий все настройки вебпака.
Для начала укажем точки входа и выхода проекта:
const path = require('path');
module.exports = {
entry: './src/index.js', // Указываем точку входа - главный модуль приложения,
// в который импортируются все остальные
output: {
path: path.resolve(__dirname, 'dist'), // Директория, в которой будет
// размещаться итоговый бандл, папка dist в корне приложения
clean: true, // Очищает директорию dist перед обновлением бандла
// Свойство стало доступно с версии 5.20.0, до этого использовался
// CleanWebpackPlugin
},
}
Настраиваем webpack для разработки
Устанавливаем webpack-dev-server
- инструмент, позволяющий не перезапускать вебпак после каждого изменения. Это сервер, хранящий данные в памяти(вы не сможете увидеть их в папке dist
) и запускающий ваше приложение на localhost
(порт по умолчанию :8080
)
npm i -D webpack-dev-server
Также для разработки будем использовать devtool: 'source-map'
. Поскольку вебпак собирает все модули в один бандл, может быть весьма проблематично понять, что у нас за ошибка на 1593 строке минифицированного кода. Именно эта проблема решается при помощи source-map
, благодаря этой настройке нумерация строк и названия функций и переменных в инструментах разработчика отображаются как в исходном коде.
Дополним module.exports
в webpack.config.js
следующими свойствами:
devtool: 'source-map',
devServer: {
hot: true, // Включает автоматическую перезагрузку страницы при изменениях
}
Теперь перейдем в файл package.json
и создадим скрипты для нашего проекта:
{
// ...
"scripts": {
"start": "webpack serve", // Запускает webpack-dev-server
"build": "webpack", // Собирает проект в режиме разработки
"build-prod": "webpack --mode=production", // собирает проект для продакшена
"clean": "rd /s /q dist" // удаляет директорию dist
}
// ...
}
Режимы сборки
В webpack есть два режима сборки проекта: development
и production
.
development
- режим для разработки, максимальная скорость сборки, низкая производительность приложения.production
- режим для продакшена, медленная сборка, высокая производительность приложения.
Режим сборки указывается в свойстве mode
в настройках webpack, для правильной работы скриптов в webpack.config.js
внесем следующие изменения:
const path = require('path');
let mode = 'development'; // По умолчанию режим development
if (process.env.NODE_ENV === 'production') { // Режим production, если
// при запуске вебпака было указано --mode=production
mode = 'production';
}
module.exports = {
mode, // Сокращенная запись mode: mode в ES6+
entry: './src/index.js',
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
devServer: {
hot: true,
},
}
Плагины и загрузчики
Именно благодаря плагинам и загрузчикам webpack является действительно мощным инструментом, ведь по умолчанию вебпак умеет обрабатывать только js и json.
Загрузчики (loaders) предоставляют возможность работать не только с базовыми js и json, но с практически любым типом данных. Устанавливаются в
module.rules
.Плагины в некотором смысле являются более мощной версией загрузчиков, они выполняются после сборки бандла и предоставляют широкие возможности. В конфигурацию следует передавать новый экземпляр плагина через
new
. Устанавливаются вplugins
Ассеты
Ассеты(Asset Modules) - одно из нововведений webpack 5, которое позволяет избавится от file-loader
, url-loader
и raw-loader
. Подробнее про ассеты вы можете почитать здесь.
Для комфортного использования ассетов обновим output
в webpack.config.js
:
output: {
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'assets/[hash][ext][query]', // Все ассеты будут
// складываться в dist/assets
clean: true,
}
Добавляем поддержку HTML
Для поддержки HTML нам понадобится загрузчик html-loader
и html-webpack-plugin
:
npm i -D html-loader html-webpack-plugin
Вносим изменения в webpack.config.js
:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // Импортируем плагин
let mode = 'development';
if (process.env.NODE_ENV === 'production') {
mode = 'production';
}
const plugins = [
new HtmlWebpackPlugin({
template: './src/index.html', // Данный html будет использован как шаблон
}),
]; // Создаем массив плагинов
module.exports = {
mode,
plugins, // Сокращенная запись plugins: plugins в ES6+
entry: './src/index.js',
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
devServer: {
hot: true,
},
module: {
rules: [
{ test: /\.(html)$/, use: ['html-loader'] }, // Добавляем загрузчик для html
],
}
}
html-webpack-plugin
автоматически импортирует главный javascript-файл в документ, поэтому нет необходимости это делать вручную.
Добавляем поддержку стилей
Для поддержки стилей нам понадобится mini-css-extract-plugin
, css-loader
, sass-loader
/less-loader
(В зависимости от используемого вами препроцессора), sass
/less
, postcss
, postcss-preset-env
и postcss-loader
.
npm i -D mini-css-extract-plugin css-loader sass-loader sass postcss postcss-preset-env postcss-loader
Или, если вы используете less:
npm i -D mini-css-extract-plugin css-loader less-loader less postcss postcss-preset-env postcss-loader
Начнем с создания в корне проекта файла postcss.config.js
, этот инструмент автоматически подставляет вендорные префиксы в стилях в зависимости от вашей конфигурации browserslist
(но о нем чуть позже). Из файла экпортируем данный объект:
module.exports = {
plugins: ['postcss-preset-env'],
};
Вносим изменения в webpack.config.js
:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// Импортируем плагин
let mode = 'development';
if (process.env.NODE_ENV === 'production') {
mode = 'production';
}
const plugins = [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css', // Формат имени файла
}), // Добавляем в список плагинов
];
module.exports = {
// ...
module: {
rules: [
{ test: /\.(html)$/, use: ['html-loader'] },
{
test: /\.(s[ac]|c)ss$/i, // /\.(le|c)ss$/i если вы используете less
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
}, // Добавляем загрузчики стилей
],
}
}
При использовании webpack стили импортируются не в html-файле, а непосредственно в javascript(не касается подключения через cdn):
import './styles/main.scss';
Browserslist
Browserslist - конфиг, который служит для обозначения браузеров, которые должны поддерживаться данным приложением.
В корне приложения создадим файл .browserslistrc
Подробно про конфигурацию browserslist вы можете прочитать здесь
Вы можете использовать следующую конфигурацию:
> 0.5% # На браузер должно приходится не менее 0.5% пользователей
not dead # Браузеры с официальной поддержкой
В webpack.config.js
вносим следующие изменения:
// ...
let mode = 'development';
let target = 'web'; // в режиме разработки browserslist не используется
if (process.env.NODE_ENV === 'production') {
mode = 'production';
target = 'browserslist'; // в продакшен режиме используем browserslist
}
// ...
module.exports = {
mode,
target, // Сокращенная запись target: target в ES6+,
// ...
}
Добавляем поддержку изображений и шрифтов
Для обработки изображений и шрифтов в конфигурации вебпака в module.rules
добавим следующие правила:
{
test: /\.(png|jpe?g|gif|svg|webp|ico)$/i,
type: mode === 'production' ? 'asset' : 'asset/resource', // В продакшен режиме
// изображения размером до 8кб будут инлайнится в код
// В режиме разработки все изображения будут помещаться в dist/assets
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
type: 'asset/resource',
},
Теперь, помимо классического использования в html и css, вы можете импортировать изображения напрямую в javascript. Пример использования:
import image from './assets/image.jpg';
document.getElementById('root').innerHTML = `<img src=${image} />`;
Babel
Babel - это транскомпилятор JavaScript. Мы можем использовать все новые возможности языка, а babel сделает наш код совместимым с предыдущими версиями JavaScript.
Установим необходимые для babel зависимости: @babel/core
, @babel/preset-env
и babel-loader
:
npm i -D @babel/core @babel/preset-env babel-loader
В корне проекта создадим файл babel.config.js
и экспортируем данный объект:
module.exports = {
presets: ['@babel/preset-env'],
};
Далее в webpack.config.js
добавим следующее правило в module.rules
:
{
test: /\.js$/,
exclude: /node_modules/, // не обрабатываем файлы из node_modules
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true, // Использование кэша для избежания рекомпиляции
// при каждом запуске
},
},
},
Теперь, если соберем проект:
npm run build
И посмотрим в папке dist на наш код из начала статьи в ES6+ синтаксисе, мы увидим, что теперь современные функции JavaScript заменены на полифилы:
var sayHello = function sayHello() {
return console.log('hello');
};
На этом базовая настрока webpack подходит к концу, вы можете использовать данную сборку для своих проектов. Далее будет рассмотрена настройка webpack для работы с React, а также в конце статьи вы сможете найти полезные ссылки(в том числе на github данной сборки).
[bonus] Настройка webpack для работы с React
Теперь добавим поддержку React и подключим плагин react-refresh-webpack-plugin
. Данный плагин является экспериментальным, но работает достаточно стабильно. Благодаря этому плагину при перезагрузке страницы состояния state компонентов остаются неизменными. Здесь вы можете увидеть пример работы hot reloading в React(на данном видео эта функция реализована благодаря create-react-app
).
Установим необходимые зависимости:
npm i -D @babel/preset-react cross-env react-refresh pmmmwh/react-refresh-webpack-plugin
cross-env
позволяет получить доступ к установке переменных окружения в windows.
Теперь при запуске сервера мы будем передавать SERVE=true
через переменные окружения. Внесем следующие изменения в наши скрипты:
{
// ...
"scripts": {
"start": "cross-env SERVE=true webpack serve", // передаем SERVE=true в
// process.env
"build": "webpack",
"build-prod": "webpack --mode=production",
"clean": "rd /s /q dist"
}
// ...
}
Добавляем поддержку React и плагин в babel.config.js
:
const plugins = [];
if (process.env.NODE_ENV === 'development') {
plugins.push('react-refresh/babel');
} // React hot reloading необходим только в режиме разработки
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'], // Добавляем в babel
// пресет для работы с React
plugins,
};
Последний шаг: добавим поддержку jsx и подключим плагин в webpack.config.js
:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
// Импортируем плагин
// ...
if (process.env.SERVE) { // Используем плагин только если запускаем devServer
plugins.push(new ReactRefreshWebpackPlugin());
} // Данный код должен быть размещен после объявления массива plugins
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.jsx?$/, // обновляем регулярное выражение для поддержки jsx
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
],
},
};
Заключение
Спасибо за прочтение, надеюсь эта статья помогла вам разобраться в webpack 5. На моем github размещены обе версии сборки webpack: базовая сборка и сборка для React.