Что такое Webpack

Рассказываем, как устроен самый известный бандлер и как настроить базовый проект.

Время чтения: 7 мин

Кратко

Секция статьи "Кратко"

Webpack — это самый популярный сборщик в мире JS. Его функции легко расширяются с помощью сторонних пакетов, так что Webpack можно заставить делать практически что угодно. А если готового плагина не нашлось, благодаря мощному API можно написать решение под свой уникальный случай.

Базовое использование

Секция статьи "Базовое использование"

На базовом уровне использовать Webpack довольно просто. Представим, что в нашем приложении есть файл application.js с функциями, необходимыми для работы:

        
          
          function sayHello() {  console.log("Hello!")}function sayBye() {  console.log("Bye!")}// Экспортируем эти функции,// чтобы воспользоваться ими в другом месте:export { sayHello, sayBye }
          function sayHello() {
  console.log("Hello!")
}

function sayBye() {
  console.log("Bye!")
}

// Экспортируем эти функции,
// чтобы воспользоваться ими в другом месте:
export { sayHello, sayBye }

        
        
          
        
      

Теперь создадим файл index.js, который будет использоваться как входная точка, и вызовем в нем функции приложения:

        
          
          import { sayHello, sayBye } from "./application"sayHello()sayBye()
          import { sayHello, sayBye } from "./application"

sayHello()
sayBye()

        
        
          
        
      

Настроим Webpack, чтобы он собирал единый файл с кодом приложения.

Для начала следует добавить Webpack в список зависимостей приложения:

        
          
          npm install --dev webpack webpack-cli
          npm install --dev webpack webpack-cli

        
        
          
        
      

webpack — основная зависимость, в которой хранится весь код, нужный для работы бандлера.

webpack-cli — обёртка для запуска Webpack из командной строки, Command Line Interface.

Теперь достаточно создать простой конфигурационный файл webpack.config.js:

        
          
          // path — встроенный в Node.js модульconst path = require("path")module.exports = {  // Указываем путь до входной точки:  entry: "./src/index.js",  // Описываем, куда следует поместить результат работы:  output: {    // Путь до директории (важно использовать path.resolve):    path: path.resolve(__dirname, "dist"),    // Имя файла со сборкой:    filename: "bundle.js"  }}
          // path — встроенный в Node.js модуль
const path = require("path")

module.exports = {
  // Указываем путь до входной точки:
  entry: "./src/index.js",
  // Описываем, куда следует поместить результат работы:
  output: {
    // Путь до директории (важно использовать path.resolve):
    path: path.resolve(__dirname, "dist"),
    // Имя файла со сборкой:
    filename: "bundle.js"
  }
}

        
        
          
        
      

Почти готово, осталось только добавить скрипт для сборки в package.json и вызвать его:

        
          
          "scripts": {  "build": "webpack"}
          "scripts": {
  "build": "webpack"
}

        
        
          
        
      
        
          
          npm run build
          npm run build

        
        
          
        
      

После выполнения в директории dist окажется файл bundle.js, который уже можно подключать на страницу в браузере.

В корне проекта нужно создать папку src, а в ней файл index.html и подключить в него файл со сборкой:

        
          
          <!DOCTYPE html><html>  <head>    ...  </head>  <body>    ...    <script src="./dist/bundle.js"></script>  </body></html>
          <!DOCTYPE html>
<html>
  <head>
    ...
  </head>
  <body>
    ...
    <script src="./dist/bundle.js"></script>
  </body>
</html>

        
        
          
        
      

Если открыть этот файл в браузере, то в консоли появится приветствие и прощание.

В консоли появляется приветствие и прощание.

Отслеживание изменений в проекте

Секция статьи "Отслеживание изменений в проекте"

Заново запускать сборку после внесения каждой правки не очень удобно. Во-первых, придётся каждый раз писать команду в терминале. Во-вторых, полная сборка больших проектов может занимать десятки минут, и тратить столько времени каждый раз просто расточительно.

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

Добавим новую команду в package.json:

        
          
          "scripts": {  "build": "webpack",  "watch": "webpack --watch"}
          "scripts": {
  "build": "webpack",
  "watch": "webpack --watch"
}

        
        
          
        
      

Теперь достаточно открыть файл index.html в браузере и обновлять страницу после сохранения файлов с исходным кодом.

Для большего удобства можно воспользоваться пакетом webpack-dev-server.

Расширение

Секция статьи "Расширение"

Webpack — невероятно мощный инструмент, в первую очередь за счёт своей расширяемости. В базовом случае он только собирает несколько JS-файлов в один, но с помощью лоадеров и плагинов можно сильно изменить его функциональность.

Webpack начинает свою работу со входной точки — первого JS-файла. В нём он находит все импорты, по которым собирает файлы с исходным кодом проекта. Во всех найденных файлах снова ищет импорты, и так далее, пока импорты не закончатся. В итоге у Webpack оказывается список всех файлов проекта и информация, как эти файлы связаны. Затем в дело вступают плагины и лоадеры. Для каждого файла Webpack найдёт все подходящие по конфигурации лоадеры и обработает ими файл.

Плагины — это более глобальный способ изменить поведение сборщика. В процессе сборки Webpack будет вызывать специальную функцию в каждом плагине, передавая в неё текущий контекст сборки и API для изменения этого контекста.

Обратная сторона расширяемости Webpack — сложность его конфигурации. В больших проектах файл с настройками сборки может занимать тысячи строк. Часто такую конфигурацию разбивают на несколько файлов, чтобы её было проще читать.

Лоадеры

Секция статьи "Лоадеры"

Лоадер — это функция, которая принимает содержимое какого-то файла и должна вернуть изменённое содержимое.

Например, ts-loader превратит любой TypeScript-код в обыкновенный JavaScript-код.

Для Webpack написано огромное число лоадеров — для работы со стилями, с разными языками, для обработки изображений и для многого другого. Вот несколько, которые можно встретить почти в любом проекте:

  • style-loader — импортирует CSS-файлы и внедряет стили в DOM.
  • css-loader — позволяет работать с @import и url() внутри CSS.
  • babel-loader — позволяет писать код на современном JS, но исполнять его даже в старых браузерах.

Чтобы добавить новый лоадер, нужно расширить файл webpack.config.js:

        
          
          module.exports = {  // В этом массиве будут перечислены все применяемые лоадеры:  module: {    rules: [      {        // Это правило будет применяться ко всем файлам,        // имя которых подойдет под регулярное выражение:        test: /\.css$/,        // Список лоадеров, которые применятся к файлу:        use: [          { loader: "style-loader" },          {            loader: "css-loader",            // Лоадеру можно передать параметры:            options: { modules: true }          }        ]      }    ]  }}
          module.exports = {
  // В этом массиве будут перечислены все применяемые лоадеры:
  module: {
    rules: [
      {
        // Это правило будет применяться ко всем файлам,
        // имя которых подойдет под регулярное выражение:
        test: /\.css$/,
        // Список лоадеров, которые применятся к файлу:
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            // Лоадеру можно передать параметры:
            options: { modules: true }
          }
        ]
      }
    ]
  }
}

        
        
          
        
      

Плагины

Секция статьи "Плагины"

Плагин — мощный способ расширить или изменить функциональность Webpack. Если лоадер ограничен только одной функцией (принимает содержимое файла и должен вернуть изменённое содержимое), то плагин может делать всё что угодно.

Для Webpack написано огромное количество плагинов — для работы со стилями, повышения удобства разработки и упрощения жизни инженеров, для автоматизации рутинных операций и много других. Вот несколько, которые можно встретить почти в любом проекте:

  • MiniCssExtractPlugin — по умолчанию все стили, которые обработал Webpack, попадают в JS-файл и потом вставляются в тег <style>. Этот плагин извлекает все стили в отдельный CSS-файл, который можно подключить к странице через тег <link>.
  • HotModuleReplacementPlugin — позволяет делать изменения в коде и видеть изменения в браузере без полной перезагрузки страницы, это делает разработку более комфортной.
  • CompressionWebpackPlugin — сжимает все ресурсы, сгенерированные Webpack, чтобы передавать пользователям по сети меньший объём данных.

Чтобы добавить новый плагин в сборку, нужно расширить файл webpack.config.js:

        
          
          // Webpack предоставляет несколько плагинов в основном пакете:const { ProgressPlugin } = require("webpack")module.exports = {  plugins: [    // При сборке этот плагин будет отображать прогресс в консоли:    new ProgressPlugin()  ]}
          // Webpack предоставляет несколько плагинов в основном пакете:
const { ProgressPlugin } = require("webpack")

module.exports = {
  plugins: [
    // При сборке этот плагин будет отображать прогресс в консоли:
    new ProgressPlugin()
  ]
}

        
        
          
        
      

На практике

Секция статьи "На практике"

Игорь Камышев

Секция статьи "Игорь Камышев"

📄 На практике не очень удобно вручную создавать HTML-файлы и подключать туда сборки, сгенерированные Webpack — ведь количество и имена файлов могут меняться в зависимости от сборки. Для автоматизации часто используют HtmlWebpackPlugin.

В первую очередь, нужно установить пакет с плагином в проект:

        
          
          npm install --save-dev html-webpack-plugin
          npm install --save-dev html-webpack-plugin

        
        
          
        
      
        
          
          const { HtmlWebpackPlugin } = require("html-webpack-plugin")module.exports = {  plugins: [new HtmlWebpackPlugin()]}
          const { HtmlWebpackPlugin } = require("html-webpack-plugin")

module.exports = {
  plugins: [new HtmlWebpackPlugin()]
}

        
        
          
        
      

При сборке плагин сгенерирует пустой index.html в папку с собранным проектом и добавит туда ссылки на финальные JS- и CSS-файлы. При необходимости плагин можно кастомизировать.

💫 path — это встроенный в Node.js модуль для работы с путями. Чтобы не беспокоиться об особенностях разных операционных систем, лучше всегда при работе с путями в файловой системе использовать модуль path.

В нём есть несколько полезных для конфигурации Webpack функций:

  • path.resolve — функция, которая принимает любое число сегментов пути и возвращает абсолютный путь. Работает так:
        
          
          path.resolve("/foo/bar", "./baz")// "/foo/bar/baz"path.resolve("/foo/bar", "/tmp/file/")// "/tmp/file"// Если текущая рабочая директория /home/user:path.resolve("www", "static_files/png", "../gif/image.gif")// "/home/user/www/static_files/gif/image.gif"
          path.resolve("/foo/bar", "./baz")
// "/foo/bar/baz"
path.resolve("/foo/bar", "/tmp/file/")
// "/tmp/file"

// Если текущая рабочая директория /home/user:
path.resolve("www", "static_files/png", "../gif/image.gif")
// "/home/user/www/static_files/gif/image.gif"

        
        
          
        
      
  • path.sep — строка разделителя путей в текущем окружении (/ или \).

  • path.extname — функция, которая принимает строку с именем файла и возвращает расширение этого файла.

Подробная документация по модулю path.