Webpack - простой учебник.
Webpack - это не просто. Это может сбивать с толку, и это замешательство может привести к чувству разочарования и усталости от JS. Усталость возникла из-за необходимости реализовать / изучить «еще кое-что». Однако Webpack как инструмент для современных инженеров, безусловно, заслуживает изучения.
Примечание: на основе webpack v1, скоро будет обновлен до v2. TL; DR & Покажите мне код ?: Публичное репо для проекта можно найти здесь.
Это наиболее простой и всеобъемлющий учебник по Webpack, который включает среду разработки и производственную настройку в CI. Это поможет:
- Поддерживайте продуктивность своей команды.
- Ускорьте разработку прототипов.
- Избегайте зацикливания на деталях конфигурации.
Это может быть немного длинновато, но Webpack недостаточно прост, чтобы быть кратким. Итак, приступим к делу.
Что за Webpack?
Документация Webpack исчерпывающая, но многословная, что может означать крутой период обучения. Webpack имеет так много функций и подходит для такого количества случаев использования, что когда вы пытаетесь найти эталонный проект на github, вы всегда найдете новый вариант. Это инструмент Jankee Candle из JS, и с некоторым разочарованием все начинает пахнуть.
Что мы пытаемся сделать на высоком уровне?
Возьмем базовый вариант использования javascript переднего плана. Обычное включение некоторой внешней библиотеки (например, jQuery) в конце тела нашего обслуживаемого index.html.
<!doctype html> <html> <head> <title>Webpack App</title> </head> <body> <div id='root'></div> <!-- Traditional jQuery --> <script src="...cdn/jquery.min.js"></script> <!-- Our bundled app --> <script src="/dist/bundle.js"></script> </body> </html>
Если мы пишем современное интерфейсное приложение, используя, например, React вместо jQuery, мы создадим кучу файлов JS и ресурсов, которые описывают наши компоненты и логику приложения, но наша конечная цель будет той же. Нам понадобится скомпилированный JS-файл наших объединенных ресурсов, на которые есть ссылки в нашем html, чтобы он мог работать с DOM так же, как мы делаем с jQuery.
Именно здесь на помощь приходит Webpack. Webpack заботится об этом преобразовании ресурсов в пригодный для использования клиентский скрипт. Webpack рассматривает все как модули, поэтому, как мы можем require ('./ file.js'), мы можем также require ('./ styles. css).
Webpack сводится к этой концепции высокого уровня:
Модули с зависимостями в → Static Assets out.
У вас есть модули с зависимостями:
- JS файлы, используя ваши собственные модули, а также модули с открытым исходным кодом.
- Таблицы стилей
- Изображения и шрифты
Вам нужны статические активы:
- Связанные файлы статических ресурсов, которые будут работать в браузере при использовании в разметке.
Webpack позаботится об этом преобразовании и сделает это в соответствии с инструкциями, которые мы можем ему дать.
Конфигурация Webpack
Webpack настраивается объектом JSON. Этот объект описывает Webpack, как мы ожидаем, что он будет работать в нашем случае использования. Следующий объект является каркасом объекта конфигурации Webpack. Давайте обсудим, на что ссылается каждый ключ и какие значения они ожидают:
{ target: '', devtool: '', resolve: { root:, extensions: } entry: [], output: { }, plugins: [ ], module: { loaders: [] } }
У нас будет версия этого файла конфигурации для каждой из наших сред:
- webpack.dev.config
- webpack.prod.config
Цель
Webpack дает вам возможность объявить целевую среду, в которой вы собираетесь запускать созданные ресурсы. В какой из следующих сред будут работать ваши статические ресурсы?
web
webworker
узел
асинхронный узел
узел-webkit
электронный
электронный рендерер
По умолчанию это Интернет, и мы будем его использовать.
DevTool
Эта опция сообщает Webpack, какой файл отладки исходных карт мы хотим сгенерировать для нашего кода. Что такое исходные карты? Treehouse хорошо описывает это:
Исходная карта обеспечивает способ сопоставления кода в сжатом файле с его исходной позицией в исходном файле.
Таким образом, мы получаем возможность выбора типа исходной карты, которую мы можем сгенерировать, отлично.
Почему нас это волнует? Какой вариант использования?
Что ж, когда мы запускаем наш код в производство, мы минимизируем его, чтобы уменьшить размер файла и повысить производительность. Но при этом будет невозможно читать и, следовательно, отлаживать. Карты источников позволяют нам отменить эту обфускацию и отладить в продакшене.
Различные параметры Webpack devTool создают файлы разных размеров и сопоставления номеров строк. Исходные карты загружаются только при открытии консоли разработчика, поэтому они не будут влиять на сетевые запросы, поэтому в определенном смысле размер файла не важен, но размер файла может замедлить время компиляции при разработке, что раздражает. Фу!
Разве нам не нужен лучший вариант? Мы делаем.
Webpack предоставляет множество мелких элементов управления для различных случаев использования. Возможно, вы захотите, чтобы эти параметры были:
- cheap-module-eval-source-map в разработке
- cheap-module-source-map в производстве
Отметьте здесь для разбивки по опциям. Мне вообще нужны исходные карты? Карты исходного кода помогают отладить миниатюрный код в производственной среде. Вам решать, хотите ли вы этого, но параметры Webpack есть, и теперь вы можете принять обоснованное решение.
Решать
Мы можем сказать webpack, как разрешать модули. Что это значит? Ну, один из вариантов состоит в том, что мы можем указать webpack, что нам не нужно добавлять расширения файлов в наши операторы импорта модуля. Например:
// Before import config from './config.js' import Component from './Component.jsx' // After import config from './config' import Component from './Component'
Приятно
Вход и выход
То, что входит, должно выходить наружу. И куда он идет, должен быть известен потребителю (то есть index.html).
Точка entry - это путь к файлам, которые мы хотим обработать Webpack. Это файлы, которые мы хотим включить в сборку (например, index.js и его зависимости). Вывод - это объект, который определяет имя и местоположение, в которое мы хотим отправить результат.
{ entry: "/src/index.js", output: { path: path.join(__dirname, 'dist'), filename: `[name].[hash].js`, publicPath: '/dist/' } }
Путь - это относительный путь, по которому будут отправлены локальные скомпилированные ресурсы. publicPath - это общедоступный URL-адрес ресурсов, на которые ссылается браузер. Итак, когда браузер ищет файл изображения, где он должен искать? Если ресурсы расположены на вашем сервере, вам необходимо указать на них общедоступный URL-адрес.
yourApp ├──dist/ | ├──bundle.js ├──index.html<body>
<script src="/dist/bundle.js"></script></body>
Помните, что на этот выходной файл будет ссылка в нашем index.html. Основы остаются прежними.
<!doctype html> <html> <head> <title>Webpack App</title> </head> <body> <div id='root'></div> <script src="/dist/bundle.js"></script> </body> </html>
Расширенные функции
- Разделение кода
- Аннулирование кеша с хешированием имени файла
- Замена горячего модуля
Разделение кода. Наша точка входа в веб-пакет может быть объектом, а не строкой. Зачем нам это нужно? В Webpack встроена идея отложенной загрузки, при которой вы загружаете только то, что вам нужно, когда вам это нужно. Он имеет возможность разбить ваш код на набор файлов, которые требуются по запросу. Например, мы можем отделить js-файлы распространенных поставщиков (например, react и т. Д.) От наших собственных js-файлов.
Мы также можем объявить внешние маршруты к асинхронным загрузочным модулям. Зачем это делать? Представьте, что у нас есть 2 маршрута / profile и / feed. Когда мы посещаем профиль, нам нужно только загрузить JS, необходимый для этой страницы, а не страницу канала и наоборот. Эта инкрементная загрузка сокращает время до первого контента при начальной загрузке страницы и обеспечивает улучшенное взаимодействие с пользователем.
entry: { app: [ '/src/index', ], vendor: [ 'react' ] }
Недействительность кеша:. Мы также можем добавить уникальные хеш-имена к нашим именам файлов, чтобы при компиляции они автоматически блокировали кешированные ресурсы, поскольку имя изменилось. Представьте, что мы всегда запускаем в рабочую среду файл с именем bundle.js. Он будет кеширован браузером. Однако, если наш файл имеет уникальное имя в каждой сборке, например filename.fa6860b29d7f6010c96f.js, тогда при загрузке страницы будет загружена новая версия файла. Сделать это очень просто. Мы просто используем синтаксис интерполяции в квадратных скобках с ключевыми словами [name] и [hash]. Имя будет выводиться из того, какой файл передается в вывод во время выполнения, а ключевое слово hash просто указывает Webpack вставлять хеш-значение на место. Suh-weeeeet 🍧.
filename: '[name].[hash].js'
Горячая замена модуля:. Что насчет того, если мы хотим использовать горячую замену модуля при разработке, чтобы получить быструю обратную связь при изменении файлов? В этом случае изменение файла вызывает запись в память, а не на диск, и из-за этого нам нужно добавить путь замены горячего модуля в точку входа конфигурации webpack dev.
'webpack-hot-middleware/client'
Полный результат выглядит так:
entry: { app: [ 'webpack-hot-middleware/client', '/src/index' ], vendor: [ 'react' ] }, output: { path: path.join(__dirname, 'dist'), filename: `[name].[hash].js`, publicPath: '/dist/' }
Плагины
В Webpack есть концепция плагинов, которые преобразуют ваш код. Некоторые из них поставляются с Webpack прямо из коробки, некоторые предназначены специально для оптимизации, а некоторые имеют открытый исходный код.
Зачем мне их использовать?
Думайте о каждом плагине как о единственной задаче, которую нужно выполнить. Эту работу можно настроить разными способами, но она все же имеет одну конечную цель высокого уровня. Какой пример? Что ж, давайте обсудим те, которые мы используем в нашем webpack.config.
- HotModuleReplacementPlugin: включает горячие изменения, делая измененные фрагменты js доступными для сервера разработки.
- NoErrorsPlugin: предотвращает отправку любого созданного ресурса с ошибкой компиляции в dist.
- DedupePlugin: поиск одинаковых или похожих файлов и дедупликация их на выходе.
- UglifyJsPlugin: применить минификацию для рабочего кода.
Другой важный - HtmlWebpackPlugin. Этот плагин динамически устанавливает HTML-теги, связанные с вашим ресурсом, во время компиляции. Что это значит? Помните, что у нас есть index.html, который выглядит так:
<!doctype html> <html> <head> <title>Webpack App</title> </head> <body> <div id='root'></div> <script src="/dist/bundle.js"></script> </body> </html>
Проблема здесь в статическом имени /dist/bundle.js. В нашей конфигурации Webpack мы говорим, что хотим, чтобы наши выходные файлы имели соглашение об именах динамического хеширования, чтобы мы могли кэшировать любые развернутые ресурсы. Итак, что мы действительно хотим:
<!doctype html> <html> <head> <title>Webpack App</title> </head> <body> <div id='root'></div> <script src="/dist/filename.fa6860b29d7f6010c96f.js"></script> </body> </html>
HtmlWebpackPlugin сделает это за нас. Настраиваем его как плагин вот так. Мы указываем ему на html-файл в нашем корне src. Он захватывает его, а затем внедряет теги сценария со ссылками на любые созданные файлы ресурсов, переданные ему из Webpack. Это очень мило. Больше Webpack ✨
new HtmlWebpackPlugin({ template: path.join(__dirname, 'src/index.html'), filename : 'index.html', inject: 'body' })
Погрузчики
Загрузчики позволяют загружать ресурсы, отличные от js, в виде модулей. Нам нужно указать Webpack, как обрабатывать загрузку различных типов ресурсов, которые поддерживает наше приложение, например, этими типами ресурсов могут быть .js, .css, .scss, .json, .png, .jpg и т. Д. Давайте рассмотрим пример. комплект погрузчиков. Простое описание загрузчика имеет 3 ключа верхнего уровня
- test: регулярное выражение для целевых файлов с расширением, которое соответствует указанному значению.
- загрузчик: имя используемого загрузчика. Список загрузчиков может быть применен в цепочке справа налево, где разделителем списка является!
- include: путь к файлу для ссылки на эти ресурсы из
module: { loaders: [ { test: /\.js?$/, loader: 'babel', include: path.join(__dirname, 'src') }, { test: /\.scss?$/, loader: 'style!css!sass', include: path.join(__dirname, 'src', 'styles') }, { test: /\.(png|jpg|gif)$/, loader: 'url?limit=8192' }, { test: /\.(ttf|eot|woff(2)?)(\?[a-z0-9]+)?$/, loader: 'file' }, { test: /\.svg(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml' } ] }
У каждого загрузчика будет соответствующий модуль, включенный в package.json. Например:
"dependencies": { "babel-loader": "*", "css-loader": "*", "file-loader": "*", "sass-loader": "*", "style-loader": "*", "url-loader": "*" }
Как это работает на практике? Хорошо, если вы импортируете модуль css в модуль js:
import ‘./styles/app.scss’
Webpack это увидит и пропустит через описанные загрузчики справа налево.
style!css!sass
- Sass, затем будут загружены файлы css и их зависимости.
- Затем они будут переданы загрузчику стилей, который вставит тег сценария css в html.
Загрузка файлов
Загрузка файлов с помощью Webpack имеет некоторые нюансы. Когда Webpack встречает ссылку на URL-адрес в ваших таблицах стилей, он создает относительный путь к нему в вашем распределенном пакете.
entry: { app: [ 'webpack-hot-middleware/client', '/src/index' ], vendor: [ 'react' ] }, output: { path: path.join(__dirname, 'dist'), filename: `[name].[hash].js`, publicPath: '/dist/' }
При производстве этот общедоступный путь dist лучше всего задавать как абсолютный путь к местоположению актива, как показано ниже.
Для этого мы можем обновить наши конфигурационные файлы Webpack для каждой среды следующим образом:
const ip = require('ip') const IS_DEV = process.env.NODE_ENV !== 'production'; const PORT = IS_DEV ? 8080 : process.env.PORT; const HOST = ip.address() // webpack.dev.config.js output: { path: path.join(__dirname, 'dist'), filename: `[name].[hash].js`, publicPath: `https://${HOST}:${PORT}/dist/` } // webpack.prod.config.js output: { path: path.join(__dirname, 'dist'), filename: `[name].[chunkhash].js`, publicPath: `https://pe-fe-boilerplate staging.herokuapp.com/dist/` }
Теперь мы обрабатываем общедоступный путь вывода активов, как если бы это был CDN. Это удобно, потому что, когда вы решите отправить свои производственные статические ресурсы поставщику CDN, например Cloudfront и т. Д., Вы можете просто обновить выходной publicPath в webpack. prod.config.js. 🚀
Транспиляция
Хотите использовать ES6 в своем приложении с Webpack? Нам потребуется настроить транспилятор, чтобы синтаксис ES6 был преобразован в совместимый с браузером JS. Для этого мы:
- Добавьте загрузчик для файлов JS, который использует загрузчик babel.
- Определите .bablerc, на который загрузчик babel будет ссылаться для выполнения транспиляции.
Для этого нам нужны зависимости, связанные с babel, в package.json.
"dependencies": { "babel": "*", "babel-cli": "*", "babel-loader": "*", "babel-preset-es2015": "*", "babel-preset-react": "*", "babel-preset-react-hmre": "*", "babel-preset-stage-0": "*" }
Переход к производству
Хорошо, все отлично, мы используем Webpack для загрузки различных типов ресурсов, и результат динамически вставляется в наш html. Но если приложение недоступно по общедоступному URL-адресу, может ли оно вообще быть приложением? 🌲🤔🌲 Давайте настроим наше приложение так, чтобы оно имело среду разработки и производственную среду, как у больших детей.
Настройка Экспресс
Мы будем использовать экспресс для обслуживания приложения в разработке и производстве.
В процессе разработки мы будем использовать две важные вещи, каждая из которых имеет параметры конфигурации:
- WebpackDevMiddleware: это будет обслуживать наши скомпилированные ресурсы из памяти, а не с диска.
- WebpackHotMiddleware: это включит перезагрузку горячего фрагмента. Внесите изменения, отразите их в приложении.
В процессе разработки мы укажем express, чтобы он отвечал на все запросы индексным файлом, прочитанным из файловой системы промежуточного программного обеспечения. Итак, пользователь попадает в корневой путь, и он получает index.html из файловой системы промежуточного программного обеспечения.
app.get('*', function response(req, res) { res.write(middleware.fileSystem.readFileSync(INDEX_PATH)); res.end(); });
В процессе производства мы укажем, что express должен обслуживать созданные ресурсы из общедоступного каталога (который является выходным каталогом webpack для dist).
const publicPath = express.static(path.join(__dirname, 'dist')) app.use('/dist', publicPath) app.get('/', function (_, res) { res.sendFile(INDEX_PATH) })
Чтобы увидеть все подробности, просмотрите server.js в примере репозитория.
Могу ли я передать dist в систему управления версиями?
Да, но я игнорирую это при локальной разработке. Я добавляю dist в .git / info / exclude,, поэтому, когда я запускаю Webpack локально, мои изменения в dist не отслеживаются. Вместо этого, я позволяю настройке CI позаботиться о следующем:
- Запуск тестов
- Запуск наращивания производственных активов
- Передача этих активов в систему контроля версий
- Развертывание на Heroku
Этот рабочий процесс действительно хорош. Не знаете, как настроить CI? Посмотрите мой другой пост по этому поводу здесь. Мой любимый CI-сервис - Team wercker. Отличный инструментарий и отличная поддержка. Разница между настройками Wercker в руководствах заключается в том, что в wercker.yml в этом проекте есть сценарии для обоих:
- Скомпилировать приложение и активы
- Зафиксировать активы
Если у вас есть вопросы по настройке, вы можете задать их здесь.
Покажи мне код
С публичным репо проекта можно ознакомиться здесь.
Woot! Официально освоен Webpack. Благодаря этому вы теперь знаете, как использовать Webpack для локальной разработки и производства, без обычной настройки конфигурации из 1000 строк, которую вы можете встретить в других общедоступных репозиториях. У меня есть несколько седых волос от инструментов JS, надеюсь, этот урок поможет сохранить ваш здоровый вид 🐴.
Хотите узнать, как настроить CI, распределить созданные вами активы в CDN или как настроить переменные среды для сборок в React Native? Подпишитесь, чтобы узнать о них!
Им Шейн, веб-разработчик. Зацени меня здесь.
Или мой Github 🚀