Webpack
Webpack - это самый популярный на сегодняшний день сборщик модулей (bundler) для
JavaScript-приложений, предоставляющий широкие возможности для тонкой настройки процесса сборки приложения.
Пример оптимальной настройки Webpack для JS/React/TS-проектов.
Файл с настройками, обычно, носит название webpack.config.js и экспортируется в виде CommonJS-модуля:
// webpack.config.js
module.exports = {
  // config
}
Для сборки требуется установить webpack и webpack-cli и выполнить команду webpack.
Команды и флаги Webpack CLI
Основные команды
- serve- запуcкает сервер для разработки (- webpack-dev-server)
- watch- запускает вебпак и следит за изменениями файлов
Основные флаги
- --entry- входная точка приложения (см. ниже), например:- webpack --entry ./src/index.js
- --config, -c- путь к файлу с настройками, например:- webpack -c ./config/webpack.dev.js
- --merge, -m- объединение двух и более файлов с настройками, например:- webpack -c ./webpack.common.js -c ./webpack.dev.js -m
- --progress- отображение прогресса сборки, например:- webpack --progress
- --output-path, -o- директория для сборки (см. ниже), например:- webpack -o ./build
- --watch, -w- наблюдение за файлами, например:- webpack -w ./app.js
- --hot, -h- включает "горячую" замену модулей (hot modules replacement)
- --devtool, -d- управление созданием карт ресурсов (source maps)
- --mode- режим сборки, например:- webpack --mode=development
- --analyze- вызов- webpack-bundle-analyzerдля получения информации о сборке, например:- webpack --analyze
Настройки
mode
mode определяет режим сборки. Режим, в свою очередь, определяет, какие инструменты вебпак будет использовать для генерации "бандла". Это могут быть, например, инструменты для минимизации и оптимизации кода (при производственном режиме).
mode: 'development'
// или
mode: 'production'
context
context определяет контекст сборки - основную директорию, абсолютный путь для разрешения входных точек и загрузчиков. Используется редко.
context: path.resolve(__dirname, 'src')
entry
entry определяет входную точку (точки) приложения. Входная точка - это основной JS-файл, в котором импортируются модули приложения. Значением entry может быть строка, массив, объект или функция. По умолчанию, имеет значение index.js.
entry: path.resolve(__dirname, 'src/app.js')
// или
entry: ['./app.js', './test.js']
// или
// названия файлов сборки
entry: {
  app: './app.js',
  home: {import: './home.js', filename: 'pages/[name][ext]'},
  about: {import: './about.js', filename: 'pages/[name][ext]'}
}
// или
// зависимости
entry: {
  app: {import: './app.js', dependOn: 'react-vendors'},
  'react-vendors': ['react', 'react-dom', 'prop-types']
}
output
output определяет директорию, в которую помещаются файлы сборки. Значением output может быть строка или объект. По умолчанию, имеет значение dist.
output: path.resolve(__dirname, 'build')
// или
output: {
  // основные
  // очистка директории перед повторной сборкой
  // замена clean-webpack-plugin
  clean: {
    // сохранение файлов
    keep: /\ignored\/dir\//
  }
  // названия файлов
  // development
  filename: '[name].bundle.js',
  // production
  filename: '[name].[contenthash].bundle.js'
  // путь к директории
  path: path.resolve(__dirname, 'build'),
  // путь к файлам в index.html - относительный или абсолютный
  publicPath: './',
  // дополнительные
  // загрузка "чанков" из разных источников
  crossOriginLoading: 'anonymous',
  // экспериментальная возможность
  // к скриптам добавляется type="module"
  module: true,
  // возможности JS, которые могут использоваться в генерируемых файлах
  environment: {
    arrowFunction: true,
    bigIntLiteral: false,
    const: true,
    destructuring: true,
    dynamicImport: false,
    forOf: true
  }
}
resolve
resolve определяет порядок разрешения модулей. Значением resolve является объект.
resolve: {
  // основные
  // синонимы для импорта (import, require)
  // файл src/components/Home.js можно импортировать так:
  // import Home from '@/Home'
  alias: {
    '@': path.resolve(__dirname, 'src/components')
  },
  // файлы src/utilities/format.js и src/templates/layout.js можно импортировать так:
  // import { format, layout } from '_'
  alias: {
    _: [
      path.resolve(__dirname, 'src/utilities/'),
      path.resolve(__dirname, 'src/templates/')
    ]
  },
  // расширения модулей
  extensions: [
    '.mjs',
    '.js',
    '.jsx',
    '.ts',
    '.tsx',
    '.vue',
    '.json'
  ],
  // дополнительные
  // название файла, используемое при разрешении директории
  // по умолчанию index
  mainFiles: ['main'],
  // директория с модулями
  // по умолчанию node_modules
  modules: ['app_modules']
},
Только для development-режима
devtool
devtool определяет стиль карт ресурсов (source maps), т.е. способ их создания.
// оптимальный вариант
// с точки зрения скорости и содержательности
devtool: 'eval-cheap-source-map'
devServer
devServer определяет настройки для webpack-dev-server (сервера для разработки). Сервер запускается командой serve.
devServer: {
  // основные
  // gzip-сжатие обслуживаемых (served) файлов
  compress: true,
  // директория с файлами для обслуживания
  // строка или массив
  contentBase: path.resolve(__dirname, 'public'),
  // или
  contentBase: [
    path.resolve(__dirname, 'public'),
    path.resolve(__dirname, 'assets'),
  ],
  // вместо ошибки 404 (запрашиваемая страница отсутствует),
  // возвращается index.html
  historyApiFallback: true,
  // "горячая" замена модулей
  // требуется webpack.HotModuleReplacementPlugin
  hot: true,
  // открыть браузер после начала обслуживания файлов
  open: true,
  // или
  open: {
    app: ['Google Chrome', '--incognito', '--other-flags']
  },
  // порт
  port: 3000,
  // дополнительные
  // уровень сообщений, выводимых в консоль браузера
  clientLogLevel: 'silent',
  // перенаправление запросов
  proxy: {
    '/api': 'http://localhost:5000'
  },
  // осуществляется сборка обслуживаемых файлов
  // файлы помещаются в директорию, указанную в output.path
  // при повторной сборке могут возникнуть проблемы,
  // связанные с тем, что целевая директория не является пустой
  writeToDisk: true
}
Только для production-режима
optimization
optimization определяет оптимизацию сборки. Все необходимые оптимизации выполняются вебпаком автоматически (минимизация, разделение кода на части, создание распределенного, совместно используемого кода и т.д.).
optimization: {
  // создание распределенного файла времени выполнения (runtime)
  runtimeChunk: 'single'
}
perfomance
perfomance позволяет накладывать ограничения на размер генерируемых файлов.
performance: {
  // подсказки
  hints: 'warning',
  // максимальный размер входной точки в байтах
  // по умолчанию 250000
  maxEntrypointSize: 512000,
  // максимальный размер статических ресурсов
  // по умолчанию 250000
  maxAssetSize: 512000
}
Опциональные
watch
watch включает режим наблюдения за файлами. Это означает, что после сборки вебпак будет следить за изменениями.
// по умолчанию false
watch: true
watchOptions
watchOptions определяет настройки режима наблюдения.
watchOptions: {
  // осуществлять повторную сборку через секунду после обнаружения изменений
  aggregateTimeout: 1000,
  // игнорируемые файлы или директории
  ignored: /node_modules/,
  // проверять файлы на наличие изменений каждую секунду
  poll: 1000
}
externals
externals определяет внешние ресурсы. Данные ресурсы не включаются в сборку. В основном, используется при разработке библиотек.
externals: {
  react: 'react',
  lodash: {
    commonjs: 'lodash',
    amd: 'lodash',
    root: '_', // глобальная переменная
  }
}
experiments
experiments определяет экспериментальные возможности сборки.
experiments: {
  // разрешает использовать await без async
  topLevelAwait: true,
  // к скриптам добавляется type="modules"
  // используется совместно с output.module
  outputModule: true
}
module
По умолчанию вебпак умеет работать только с JS и JSON-файлами. Для обработки других файлов требуются loaders ("лоадеры", загрузчики). Такие загрузчики определяются в module.rules. module.rules имеет следующий формат:
module: {
  rules: [
    {
      // регулярное выражение для поиска файлов,
      // подлежащих преобразованию
      // CSS и SASS-файлы (.css, .scss или .sass)
      // без учета регистра
      test: /\.(c|sa|sc)ss$/i,
      // игнорируемые директории
      exclude: /node_modules/,
      // целевые директории
      // используется редко
      include: /app_modules/,
      // несколько загрузчиков
      // загрузчики применяются снизу вверх,
      // т.е. от последнего к первому
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            // количество предшествующих загрузчиков
            importLoaders: 1
          }
        },
        'sass-loader'
      ],
    }
    // или
    // один загрузчик
    // сокращение для use: [ { loader } ]
    {
      loader: 'css-loader',
      options: {
        modules: true
      }
    },
    // для обработки статических ресурсов достаточно указать type: 'asset'
    {
      test: /\.(jpe?g|png|gif|svg|eot|ttf|woff?2)$/i,
      type: 'asset'
    }
  ]
}
loaders
Каждый загрузчик устанавливается отдельно и имеет собственный набор опций.
Стилизация
- style-loader- применение сгенерированных- css-loaderстилей к DOM
- css-loader- преобразование CSS, включая- @importи- url()
- 
sass-loader
- 
less-loader
Транспиляция
- babel-loader- преобразование "нового" JS и JSX в "старый" JS
- ts-loader- преобразование TypeScript в JavaScript
Шаблонизация
- html-loader- экспорт HTML в виде строки
- markdown-loader- преобразование MD в HTML
- react-markdown-loader- преобразование MD в React-компоненты без состояния
Фреймворки
- vue-loader- загрузка и компиляция Vue-компонентов
- angular2-template-loader- загрузка и компиляция Angular-компонентов
Пример применения загрузчиков
const babelLoader = {
  loader: 'babel-loader',
  options: {
    presets: ['@babel/preset-env', '@babel/preset-react'],
    plugins: ['@babel/plugin-proposal-class-properties']
  }
}
module.exports = {
  // ...
  module: {
    rules: [
      // JavaScript, React
      {
        test: /\.m?jsx?$/i,
        exclude: /node_modules/,
        use: babelLoader
      },
      // TypeScript
      {
        test: /.tsx?$/i,
        exclude: /node_modules/,
        use: [babelLoader, 'ts-loader']
      },
      // CSS, SASS
      {
        test: /\.(c|sa|sc)ss$/i,
        exclude: /node_modules/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { importLoaders: 1 }
          },
          'sass-loader']
      },
      // MD
      {
        test: /\.md$/i,
        use: ['html-loader', 'markdown-loader']
      },
      // статические файлы
      {
        test: /\.(jpe?g|png|gif|svg|eot|ttf|woff2?)$/i,
        type: 'asset'
      }
    ]
  }
}
plugins
Плагины позволяют выполнять определенные операции в процессе сборки. Каждый плагин устанавливается отдельно и имеет собственный набор опций. Обратите внимание, что многие плагины несовместимы с вебпаком 5 версии.
Основные
- copy-webpack-plugin- копирование файлов и директорий в output.path
- html-webpack-plugin- создание HTML-файла на основе шаблона
- mini-css-extract-plugin- создание CSS-файла для каждого JS-файла (только для production-режима)
- webpack.ProvidePlugin- автоматическая загрузка модулей
- webpack.HotModuleReplacementPlugin- "горячая" замена модулей (только для development-режима)
Дополнительные
- webpack.ProgressPlugin- кастомизация отображения прогресса сборки
- webpack.DefinePlugin- создание глобальных констант
- webpack.EnvironmentPlugin- создание переменных окружения process.env
- dotenv-webpack- создание переменных окружения .env
- webpack-bundle-analyzer- подробная информация о сборке
- imagemin-webpack-plugin- уменьшение размера изображений
Полный список загрузчиков и плагинов
Пример использования плагинов
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const Dotenv = require('dotenv-webpack')
module.exports = {
  // ...
  plugins: [
    // копируем статические файлы из public/assets в output.path/assets
    new CopyWebpackPlugin({
      patterns: [
        {
          from: `./public/assets`,
          to: 'assets'
        }
      ]
    }),
    // создаем index.html в output.path на основе public/index.html
    new HtmlWebpackPlugin({
      // <%= htmlWebpackPlugin.options.title %> в index.html
      // будет заменено на Webpack
      title: 'Webpack',
      favicon: './public/favicon.png',
      template: './public/index.html',
      filename: 'index.html'
    }),
    // делаем React глобально доступным
    new webpack.ProvidePlugin({
      React: 'react'
    }),
    // обеспечиваем доступ к переменным окружения
    new Dotenv({
      path: './config/.env'
    })
  ]
}
Дополнительные инструменты
Устанавливаются отдельно и имеют собственные настройки.
- webpack-dev-server- сервер для разработки
- webpack-merge- программное объединение двух и более файлов с настройками
- webpack-config-utils- создание гибкого файла настроек
- workbox-webpack-plugin- создание сервис-воркера