Доброго времени суток, друзья!
Представляю вашему вниманию перевод статьи «Webpack: A gentle introduction» автора Tyler McGinnis.
Перед изучением новой технологии задайте себе два вопроса:
- Зачем нужен этот инструмент?
- Какие задачи он выполняет?
Если вы не можете ответить на эти вопросы, возможно, вам не нужна изучаемая технология. Давайте попробуем ответить на эти вопросы применительно к Webpack.
Зачем нужен вебпак?
Вебпак — это сборщик модулей. Он анализирует модули приложения, создает граф зависимостей, затем собирает модули в правильном порядке в один или более бандл (bundle), на который может ссылаться файл «index.html».
App.js -> |
Dashboard.js -> | Bundler | -> bundle.js
About.js -> |
Какие проблемы решает вебпак?
Обычно, при создании приложения на JavaScript, код разделяется на несколько частей (модулей). Затем в файле «index.html» необходимо указать ссылку на каждый скрипт.
<body>
...
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="libs/react.min.js"></script>
<script src='src/admin.js'></script>
<script src='src/dashboard.js'></script>
<script src='src/api.js'></script>
<script src='src/auth.js'></script>
<script src='src/rickastley.js'></script>
</body>
Это не только утомительно, но и подвержено ошибкам. Важно не только не забыть про какой-нибудь скрипт, но и расположить их в правильном порядке. Если загрузить скрипт, зависящий от React, до загрузки самого React, приложение сломается. Вебпак решает эти задачи. Не нужно беспокоиться о последовательном включении всех скриптов.
<body>
...
<script src='dist/bundle.js'></script>
</body>
Как мы скоро узнаем, сбор модулей является лишь одним из аспектов работы вебпака. При необходимости можно заставить вебпак осуществить некоторые преобразования модулей перед их добавлением в бандл. Например, преобразование SASS/LESS в обычный CSS, или современного JavaScript в ES5 для старых браузеров.
Установка вебпака
После инициализации проекта с помощью npm, для работы вебпака нужно установить два пакета —
webpack
и webpack-cli
. npm i webpack webpack-cli -D
webpack.config.js
После установки указанных пакетов, вебпак нужно настроить. Для этого создается файл
webpack.config.js
, который экспортирует объект. Этот объект содержит настройки вебпака. module.exports = {}
Основной задачей вебпака является анализ модулей, их опциональное преобразование и интеллектуальное объединение в один или более бандл, поэтому вебпаку нужно знать три вещи:
- Точка входа приложения
- Преобразования, которые необходимо выполнить
- Место, в которое следует поместить сформированный бандл
Точка входа
Сколько бы модулей не содержало приложение, всегда имеется единственная точка входа. Этот модуль включает в себя остальные. Обычно, таким файлом является index.js. Это может выглядеть так:
index.js
imports about.js
imports dashboard.js
imports graph.js
imports auth.js
imports api.js
Если мы сообщим вебпаку путь до этого файла, он использует его для создания графа зависимостей приложения. Для этого необходимо добавить свойство
entry
в настройки вебпака со значением пути к главному файлу: module.exports = {
entry: './app/index.js'
}
Преобразования с помощью лоадеров (loaders)
После добавления точки входа, нужно сообщить вебпаку о преобразованиях, которые необходимо выполнить перед генерацией бандла. Для этого используются лоадеры.
По умолчанию при создании графика зависимостей на основе операторов
import / require()
вебпак способен обрабатывать только JavaScript и JSON-файлы. import auth from './api/auth' //
import config from './utils/config.json' //
import './styles.css' // ️
import logo from './assets/logo.svg' // ️
Едва ли в своем приложении вы решитесь ограничиться JS и JSON-файлами, скорее всего, вам также потребуются стили, SVG, изображения и т.д. Вот где нужны лоадеры. Основной задачей лоадеров, как следует из их названия, является предоставление вебпаку возможности работать не только с JS и JSON-файлами.
Первым делом нужно установить лоадер. Поскольку мы хотим загружать SVG, с помощью npm устанавливаем svg-loader.
npm i svg-inline-loader -D
Далее добавляем его в настройки вебпака. Все лоадеры включаются в массив объектов
module.rules
: module.exports = {
entry: './app/index.js',
module: {
rules: []
}
}
Информация о лоадере состоит из двух частей. Первая — тип обрабатываемых файлов (
.svg
в нашем случае). Вторая — лоадер, используемый для обработки данного типа файлов (svg-inline-loader
в нашем случае). module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' }
]
}
}
Теперь мы можем импортировать SVG-файлы. Но что насчет наших CSS-файлов? Для стилей используется
css-loader
. npm i css-loader -D
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: 'css-loader' }
]
}
}
Теперь мы можем импортировать как SVG, так и CSS-файлы. Однако для того, чтобы наши стили работали корректно, нужно добавить еще один лоадер. Благодаря
css-loader
мы можем импортировать CSS-файлы. Но это не означает, что они будут включены в DOM. Мы хотим не только импортировать такие файлы, но и поместить их в тег <style>
, чтобы они применялись к элементам DOM. Для этого нужен style-loader
. npm i style-loader -D
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }
]
}
}
Обратите внимание, что поскольку для обработки CSS-файлов используется два лоадера, значением свойства
use
является массив. Также обратите внимание на порядок следования лоадеров, сначала style-loader
, затем css-loader
. Это важно. Вебпак будет применять их в обратном порядке. Сначала он использует css-loader
для импорта './styles.css'
, затем style-loader
для внедрения стилей в DOM. Лоадеры могут использоваться не только для импорта файлов, но и для их преобразования. Самым популярным является преобразование JavaScript следующего поколения в современный JavaScript с помощью Babel. Для этого используется
babel-loader
. npm i babel-loader -D
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(js)$/, use: 'babel-loader' }
]
}
}
Существуют лоадеры почти для любого типа файлов.
Точка выхода
Теперь вебпак знает о точке входа и лоадерах. Следующим шагом является указание директории для бандла. Для этого нужно добавить свойство
output
в настройки вебпака. const path = require('path')
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(js)$/, use: 'babel-loader' }
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js'
}
}
Весь процесс выглядит примерно так:
- Вебпак получает точку входа, находящуюся в
./app/index.js
- Он анализирует операторы
import / require
и создает граф зависимостей - Вебпак начинает собирать бандл, преобразовывая код с помощью соответствующих лоадеров
- Он собирает бандл и помещает его в
dist/index_bundle.js
Плагины (plugins)
Мы рассмотрели, как использовать лоадеры для обработки отдельных файлов перед или в процессе генерации бандла. В отличие от лоадеров, плагины позволяют выполнять задачи после сборки бандла. Эти задачи могут касаться как самого бандла, так и другого кода. Вы можете думать о плагинах как о более мощных, менее ограниченных лоадерах.
Давайте рассмотрим пример.
HtmlWebpackPlugin
Основной задачей вебпака является генерация бандла, на который можно сослаться в
index.html
. HtmlWebpackPlugin
создает index.html
в директории с бандлом и автоматически добавляет в него ссылку на бандл. Мы назвали бандл
index_bundle.js
и поместили его в dist
. HtmlWebpackPlugin
создаст новый файл index.html
в директории dist
и добавит в него ссылку на бандл — <script src='index_bundle.js'></script>
. Здорово, правда? Поскольку index.html
генерируется HtmlWebpackPlugin
, даже если мы изменим точку выхода или название бандла, HtmlWebpackPlugin
получит эту информацию и изменить содержимое index.html
. Как нам использовать этот плагин? Как обычно, сначала его нужно установить.
npm i html-webpack-plugin -D
Далее добавляем в настройки вебпака свойство
plugins
. const path = require('path')
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(js)$/, use: 'babel-loader' }
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js'
},
plugins: []
}
Создаем экземпляр
HtmlWebpackPlugin
в массиве plugins
.
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(js)$/, use: 'babel-loader' }
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin()
]
}
EnvironmentPlugin
Если вы используете React, то захотите установить
process.env.NODE_ENV
в значение production
перед разворачиванием (деплоем) приложения. Это позволит React осуществить сборку в режиме продакшна, удалив инструменты разработки, такие как предупреждения. Вебпак позволяет это сделать посредством плагина EnvironmentPlugin
. Он является частью вебпака, так что его не нужно устанавливать. const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(js)$/, use: 'babel-loader' }
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(),
new webpack.EnvironmentPlugin({
'NODE_ENV': 'production'
})
]
}
Теперь в любом месте нашего приложения мы можем установить режим продакшна с помощью
process.env.NODE_ENV
. HtmlWebpackPlugin
и EnvironmentPlugin
— это лишь небольшая часть системы плагинов вебпака. Режим (mode)
В процессе подготовки приложения к продакшну, необходимо выполнить несколько действий. Мы только что рассмотрели одно из них — установку
process.env.NODE_ENV
в значение production
. Другое действие заключается в минификации кода и удалении комментариев для уменьшения размера бандла. Существуют специальные плагины для решения указанных задачи, но есть более легкий способ. В настройках вебпака можно установить
mode
в значение development
или production
в зависимости от среды разработки. const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './app/index.js',
module: {
rules: [
{ test: /\.svg$/, use: 'svg-inline-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(js)$/, use: 'babel-loader' }
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin()
],
mode: 'production'
}
Обратите внимание, что мы удалили
EnvironmentPlugin
. Дело в том, что после установки mode
в значение production
вебпак автоматически присваивает process.env.NODE_ENV
значение production
. Это также минифицирует код и удаляет предупреждения. Запуск вебпака
На данный момент мы знаем, как работает вебпак и как его настраивать, осталось его запустить.
У нас есть файл
package.json
, в котором мы можем создать script
для запуска webpack
. "scripts": {
"build": "webpack"
}
Теперь при выполнении команды
npm run build
в терминале будет запущен вебпак, который создаст оптимизированный бандл index_bundle.js
и поместит его в dist
. Режимы разработки и продакшна
В целом, мы закончили с вебпаком. Напоследок давайте рассмотрим, как переключаться между режимами.
При сборке для продакшна, мы хотим все оптимизировать, насколько это возможно. В случае с режимом разработки верно обратное.
Для переключения между режимами необходимо создать два скрипта в
package.json
. npm run build
будет собирать продакшн-бандл. npm run start
будет запускать сервер для разработки и следить за изменениями файлов. Если помните, мы установили
mode
в значение production
в настроках вебпака. Однако теперь нам это не нужно. Мы хотим, чтобы переменная среды имела соответствующее значение в зависимости от выполняемой команды. Немного изменим скрипт build
в package.json
. "scripts": {
"build": "NODE_ENV='production' webpack",
}
Если у вас Windows, то команда будет такой:
"SET NODE_ENV='production' && webpack"
. Теперь в настроках вебпака мы можем менять значение
mode
в зависимости от process.env.NODE_ENV
. ...
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'
}
Для сборки готового бандла для нашего приложения мы просто запускаем
npm run build
в терминале. В директории dist
создаются файлы index.html
и index_bunlde.js
. Сервер для разработки
Когда речь идет о разработке приложения принципиально важное значение имеет скорость. Мы не хотим презапускать вебпак и ждать новую сборку при каждом изменении. Вот где нам пригодится пакет
webpack-dev-server
. Как следует из названия, это вебпак-сервер для разработки. Вместо создания дирекории
dist
, он хранит данные в памяти и обрабатывает их на локальном сервере. Более того, он поддерживает живую перезагрузку. Это означает, что при любом изменении webpack-dev-server
пересоберет файлы и перезапустит браузер. Устанавливаем пакет.
npm i webpack-dev-server -D
Все, что осталось сделать, это добавить скрипт
start
в package.json
. "scripts": {
"build": "NODE_ENV='production' webpack",
"start": "webpack-dev-server"
}
Теперь у нас имеется две команды: одна для запуска сервера для разработки, другая для сборки готового бандла.
Надеюсь, статья была вам полезной. Благодарю за внимание.