Базовая настройка webpack 5 + настройка для разработки на React (вкл. React Hot Reloading).

Что, зачем и почему?

Что? Webpack - сборщик модулей для JavaScript. Является одним из мощнейших инструментов современной веб-разработки.

Зачем? Webpack позволяет комфортно создавать приложения по модульной структуре, он собирает все модули в один бандл и минифицирует его. Но это лишь поверхностное описание возможностей этого инструмента, на деле вебпак имеет значительно больше возможностей.

Почему? Потому что webpack использует свою power для сбережения вашей motivation в процессе разработки, ведь теперь больше нет необходимости беспокоится о сборке проекта, один раз настроил webpack и он все будет делать за вас!
P.S. Ну, или не один раз.
P.S.S. Ладно, точно не один раз.

Установка webpack

  1. Для начала инициализируем наш проект:

npm init -y
  1. После инициализации установим два пакета - webpack и webpack-cli:

npm i -D webpack webpack-cli
  1. В корне проекта создаем:
    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',
        ],
      }, // Добавляем загрузчики стилей
    ],
  }
}

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',
},

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 заменены на полифилы.


На этом базовая настрока 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.

Спасибо за прочтение и с наступающим 2022-м годом!

Полезные ссылки

Комментарии (7)


  1. korsetlr473
    23.12.2021 18:50

    а зачем нужен webpack в 2021 если есть тот же vite ? где он поулчается не нужен


    1. Alexufo
      23.12.2021 22:08
      +1

      там где проект довольно узкий, например с wasm либами, воркерами, которые vite обрабатывать предлагает самостоятельно, написав плагин для требуемого поведения. Он их просто не видит как зависимости.

      webpack — гарантия набитых шишек, но да, он должен быть переосмыслен если хочет в будущее.


  1. wadowad
    23.12.2021 19:24

    Извините, но откуда берётся package.json?


    1. eadenink Автор
      23.12.2021 19:26
      +1

      Добрый вечер, он создается при инициализации проекта.

      npm init -y


    1. DulkaHakana
      23.12.2021 19:42

      npm init создаёт package.json


  1. uyrij
    24.12.2021 01:15
    +1

    +Коротко и по делу.

    https://github.com/taniarascia/webpack-boilerplate/commits?author=taniarascia

    2к???? хороший шаблон webpack.config.js , у нее же ещё есть шаблоны реакт приложения.


    1. eadenink Автор
      24.12.2021 08:20

      Да, действительно очень хороший шаблон webpack, наиболее интересно выглядит структура конфига. Спасибо за рекомендацию.