Этот учебник Webpack - моя попытка задокументировать то, что я узнал, и блог, который я хотел бы найти, когда впервые начал свой путь Webpack, много месяцев назад.

Когда я впервые начал работать в ag-Grid (отличное место для работы!), Мне пришлось наращивать количество технологий и фреймворков, которые я раньше не использовал. Одним из них был Webpack - мощный пакетировщик, используемый во многих приложениях и фреймворках.

Мы здесь, в ag-Grid, используем Webpack для объединения наших собственных продуктов, а также используем его с некоторыми из наших примеров фреймворка. Хотя существуют альтернативы Webpack, он по-прежнему очень популярен, и я считаю, что с недавно выпущенной версией 2.2 он будет оставаться таковой еще некоторое время.

Введение в руководство по Webpack

Webpack - это сборщик модулей. Он принимает разрозненные зависимости, создает для них модули и объединяет всю сеть в управляемые выходные файлы. Это особенно полезно для одностраничных приложений (SPA), которые сегодня фактически являются стандартом для веб-приложений.

Весь код для блога можно найти в репозитории Учебное пособие по Webpack: понимание того, как это работает на GitHub.

Предположим, у нас есть приложение, которое может выполнять две простые математические задачи - суммировать и умножать. Мы решили разделить эти функции на отдельные файлы для упрощения обслуживания:

Результатом этого будет:

Product of 5 and 3 = 15
index.js:17 Sum of 5 and 3 = 8

Чем нам может помочь Webpack?

Зависимости - модули спешат на помощь!

Из приведенного выше кода видно, что и multiply.js, и index.js зависят от sum.js. Здесь мы можем показать иерархию зависимостей на простой диаграмме:

Если мы неверно укажем порядок в index.html, наше приложение не будет работать. Если index.js включен перед либо из других зависимостей, или если sum.js включен после, умножьте .js мы получим ошибки.

Теперь представьте, что мы масштабируем это до реального полноценного веб-приложения - у нас могут быть десятки зависимостей, некоторые из которых зависят друг от друга. Поддержание порядка стало бы кошмаром!

Наконец, используя глобальные переменные, мы рискуем другим кодом перезаписать наши переменные, что затрудняет поиск ошибок.

Webpack может преобразовывать эти зависимости в модули - они будут иметь гораздо более узкую область действия (что безопаснее). Кроме того, преобразовывая наши зависимости в модули, Webpack может управлять нашими зависимостями за нас - Webpack подключит зависимые модули в нужное время и в правильной области (мы увидим это более подробно позже).

Смерть от тысячи порезов - уменьшение трафика

Если мы посмотрим на index.html, то увидим, что нам нужно извлечь 3 отдельных файла. Это нормально, но теперь представьте еще раз, что у нас много зависимостей - конечному пользователю придется ждать, пока все зависимости не будут загружены, прежде чем можно будет запустить основное приложение.

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

Объединение и модуляризация - основные функции Webpack. С помощью плагинов и загрузчиков мы можем еще больше расширить это (мы увидим это позже), но в первую очередь это то, для чего предназначен Webpack.

Делаем зависимости доступными и связываем их

Для нашей первоначальной настройки мы будем использовать синтаксис модуля CommonJS. Есть и другие варианты (AMD, ES2015), но пока мы будем использовать CommonJS, а позже перейдем на ES2015.

CommonJS использует module.exports для экспорта или предоставления доступа к функциям или переменным в другой код. Затем он использует require для извлечения этих экспортированных значений.

Обратите внимание, что мы сделали и sum, и multiply доступными для другого кода, и мы включили эти экспортированные функции как в multiple.js, так и в index.js.

Также обратите внимание, что наш index.html теперь должен извлекать только один файл - bundle.js.

Отлично! Теперь нам больше не нужно беспокоиться о порядке зависимостей. Мы можем раскрыть то, что хотим, и сохранить конфиденциальность другого кода. Мы также сокращаем веб-вызовы с 3 (sum.js, multiply.js и index.js) до одного вызова - это поможет ускорить загрузку.

Webpack - Начальная конфигурация

Чтобы все вышеперечисленное работало, нам нужно выполнить некоторую начальную настройку Webpack:

var path = require('path');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './dist/),
        filename: 'bundle.js
    }
}

Как минимум, нам нужно сообщить Webpack, что такое точка входа в наше приложение и каким должен быть результат.

entry: Это основная точка входа в наше приложение. Здесь будет наша первоначальная загрузка и логика приложения. Webpack использует это как отправную точку для обхода своего дерева зависимостей. Он построит граф зависимостей и при необходимости создаст модули.

output.path: Абсолютный путь для результирующего пакета. Чтобы сделать эту кроссплатформенную и удобную в использовании, мы используем встроенную функцию Node.js (path). Это поможет нам динамически создать абсолютный путь относительно того места, где мы находимся.

output.filename: имя файла результирующего пакета. Это может быть что угодно, но по соглашению он называется bundle.js.

Примечание. __dirname - служебная переменная Node.js - это имя каталога текущего файла.

Смотрим на bundle.js

Просмотр полученного файла bundle.js может быть очень полезным (предварительно помеченным и прокомментированным для упрощения навигации):

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

Он выполняет __webpack_require__(0), который, глядя на массив модулей, является нашим index.js. В результате получился результат, с которого мы начали, но с гораздо более простым управлением зависимостями и меньшим объемом веб-трафика! Блестяще!

Загрузчики - делаем Webpack умнее

Webpack понимает JavaScript. Он может создавать модули и связывать JavaScript прямо из коробки, но если вы хотите использовать что-то другое, кроме JavaScript, или хотите написать что-то вроде ES2015 / ES6, вам нужно указать Webpack, как чтобы обработать это.

Точнее, нам нужно предварительно обработать эти другие языки / версии в JavaScript ES5 - версию, которую может понять Webpack.

Здесь, в ag-Grid, мы большие поклонники TypeScript, но для целей этого примера мы собираемся преобразовать наш примерный код в ES2015 и использовать Babel для преобразования - или транспиляции - нашего ES2015 код в s = ES5-совместимый JavaScript.

Babel может многое, кроме простого переноса кода ES2015 в ES5, но это выходит за рамки этого блога. Пожалуйста, посетите сайт Babel для получения дополнительной информации о Babel.

Во-первых, давайте преобразуем наш код ES5 в ES2015:

Здесь мы используем стрелочные функции, ключевое слово const, строки шаблона и формат модуля импорта / экспорта es2015, все из которых являются функциями ES2015.

Чтобы использовать Babel, нам нужно использовать Babel Loader. Загрузчики - это то, как Webpack может обрабатывать контент, кроме JavaScript. С помощью загрузчиков мы можем заставить Webpack обрабатывать многие типы файлов - CSS, изображения, код TypeScript, ES2015 и так далее.

Нам нужны 3 зависимости Babel, чтобы использовать его с Webpack:

  • babel-loader: Интерфейс между Babel и Webpack
  • babel-core: Понимает, как читать и анализировать код, а также генерировать соответствующий вывод
  • babel-preset-es2015: Правила для Babel о том, как обрабатывать код ES2015 и преобразовывать его в ES5

Конфигурация веб-пакета с установленным загрузчиком Babel выглядит так:

Поскольку у нас может быть несколько загрузчиков в Webpack, значения предоставляются в виде массива - в нашем случае изначально мы предоставляем только один загрузчик.

  • test: Нам нужно сообщить загрузчику, что мы хотим, чтобы он обрабатывал только файлы JavaScript. Мы не хотим, чтобы он искал CSS, HTML, изображения и т. Д. - только файлы JavaScript (.js). Для этого мы предоставляем регулярное выражение, которое будет соответствовать файлам .js.
  • loader: используемый загрузчик - в данном случае Babel Loader
  • exclude: Мы не хотим, чтобы Babel обрабатывал какие-либо файлы в node_modules
  • query.presets: какой пресет (или правила) Babel мы хотим применить - в нашем случае мы ищем Babel для преобразования кода ES2015

Снова посмотрев на наш bundle.js (и на этот раз только посмотрев на ту часть, которая содержит sum.js), мы увидим следующее:

/* 2 */
function(module, exports) {
    var sum = function sum(a, b) {
        return a + b;
    };
    module.exports = sum;
}

Итак, Babel Loader преобразовал наш код ES2015 обратно в код ES5 - отлично! Лучший из двух миров.

Создание хорошего внешнего вида Webpack - CSS и стили

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

Результат будет таким же, как и раньше, но на странице:

Product of 5 and 3 = 15 Sum of 5 and 3 = 8

Мы можем улучшить это с помощью CSS - давайте обеспечим, чтобы каждый результат был на новой строке, и добавим границу вокруг каждого результата.

Наш CSS будет выглядеть так:

span {
    border: 5px solid brown;
    display: block;
}

Нам нужно вставить этот CSS в наше приложение. Конечно, мы могли бы просто добавить тег link в наш html, но если мы импортируем его, а затем воспользуемся Webpack для его обработки, мы получим выгоду от того, что может предложить Webpack.

Дополнительным преимуществом импорта CSS в наш код является то, что мы (разработчики) можем видеть связь между CSS и его использованием. Стоит отметить, что CSS не привязан к модулю, в который он импортируется (он по-прежнему глобальный), но с точки зрения разработчиков взаимосвязь более ясна.

Единственное изменение по сравнению с предыдущим - теперь мы импортируем CSS.

Нам нужны два загрузчика для обработки нашего CSS:

  • css-loader: Знает, как обрабатывать импорт CSS - берет импортированный CSS и загружает содержимое файла.
  • style-loader: берет данные CSS (из импорта) и добавляет их в документ HTML.

Теперь наша конфигурация Webpack выглядит так:

  • test: как и раньше, нам нужно сообщить загрузчикам, что мы хотим, чтобы он обрабатывал только файлы CSS - это регулярное выражение будет обрабатывать только файлы .css
  • loaders: используемые загрузчики. Обратите внимание, что на этот раз это множественное число, поскольку мы предоставляем массив загрузчиков. Также обратите внимание, что Webpack обрабатывает загрузчики в порядке справа налево, поэтому результаты css-loader (содержимое файла) передаются в style-loader (добавление стилей в документ HTML).

Если мы теперь запустим Webpack и перезагрузим наше приложение, результаты будут выглядеть так:

За кулисами эти два загрузчика динамически добавляли стили в документ HTML. Если мы проверим полученный HTML-код в Chrome, мы увидим следующее:

Это умно, но есть и другие способы обработки CSS. Мы можем разделить CSS на очистку кеша (файлы с уникальными хэшами), а затем включить эти файлы в наш результирующий пакет.

А пока давайте просто извлечем CSS и выведем его в файл, который мы затем сможем импортировать. Для этого мы воспользуемся плагином: ExtractTextPlugin.

Загрузчики используются для предварительной обработки данных перед их выводом в наш пакет. Однако плагины могут препятствовать появлению вывода в нашем пакете.

Теперь наша конфигурация Webpack выглядит так:

Вверху мы импортируем ExtractTextPlugin. Мы также изменили загрузчик для CSS, чтобы использовать этот плагин:

{
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('css-loader')
}

Это указывает Webpack передать результаты из загрузчика css в ExtractTextPlugin. Внизу настраиваем плагин:

plugins: [
    new ExtractTextPlugin('style.css')
]

Это сообщает плагину, что все переданные ему данные сохраняются в файл с именем style.css. Это может показаться не сразу полезным, но, как и раньше со многими отдельными файлами JavaScript, представьте, что у нас есть много файлов CSS. Выполнив вышеуказанное, мы можем объединить множество отдельных файлов CSS в один файл, уменьшив количество веб-вызовов, необходимых во время загрузки.

Посмотрев на dist / style.css, мы увидим:

span {
    border: 5px solid brown;
    display:block;
}

Что, конечно же, является содержанием нашего CSS. Чтобы использовать это, нам нужно изменить наш index.html, чтобы импортировать этот CSS:

<html>
<head>
    <link rel="stylesheet" href="dist/style.css"/>
    <script src="./dist/bundle.js""></script>
</head>
</html>

Результат будет таким же, как и раньше.

Одна картинка стоит тысячи слов

Давайте добавим несколько изображений в наше приложение и заставим Webpack (вместе с подходящим загрузчиком) обработать их за нас.

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

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

  • image-webpack-loader: попытается автоматически сжимать для нас большие изображения
  • url-loader: будет вставлять результаты из image-webpack-loader, если результаты маленькие, и включать изображение в выходной каталог, если они большие

У нас есть два новых изображения, которые мы хотим добавить - multiply.png, который относительно большой (около 32 КБ), и sum.png, который относительно мал (около 13 КБ).

Во-первых, давайте добавим новый служебный класс изображения - это создаст для нас новое изображение и добавит его в документ:

Давайте импортируем как новую утилиту изображений, так и изображения, которые мы хотим добавить в наше приложение:

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

  • output.publicPath Позволяет URL-загрузчику знать, какой префикс добавить для файлов, которые будут сохранены на диск. Например, в результате img.src будет img.src = 'dist / output_file.png'
  • test: как и раньше, нам нужно сообщить загрузчикам, что мы хотим, чтобы он обрабатывал только файлы изображений - это регулярное выражение будет обрабатывать только файлы .png. Мы можем сделать это более сложным, добавив поддержку других форматов изображений, для наших целей это простое регулярное выражение будет делать
  • loaders: наши загрузчики для использования - помните, что Webpack обрабатывает загрузчики в порядке справа налево, поэтому результаты image-webpack-loader будут переданы в url-loader

Если мы сейчас запустим Webpack, мы увидим что-то вроде следующего:

38ba485a2e2306d9ad96d479e36d2e7b.png
bundle.js
style.css

Если мы откроем 38ba485a2e2306d9ad96d479e36d2e7b.png, мы обнаружим, что это наше большое изображение - multiply.png. Меньшее изображение, sum.png, было встроено в bundle.js следующим образом:

module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAMAAAACDyzWAAAC6FBMVEUAuv8AgL...."

Что было бы эквивалентно наличию:

img.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAMAAAACDyzWAAAC6FBMVEUAuv8AgL...'

Когда мы запускаем наше приложение, вывод будет:

Из этого руководства по Webpack вы можете увидеть, что Webpack может предложить нам как разработчикам приложений. С помощью довольно небольшого количества настроек мы смогли обработать код ES2015, связать его, обработать CSS и обработать как большие, так и маленькие изображения, и все это простым для понимания методом.

Мы достигли всего этого и только прикоснулись к возможностям Webpack. Мы можем минимизировать и уточнить код, разделить код на имя файла для очистки кеша, обработать TypeScript и Angular - есть так много вариантов!

Попробуйте Webpack - я уверен, что вы найдете его незаменимым инструментом в своем наборе инструментов разработчика.

Узнайте больше о ag-Grid.