От автора: во второй части мы подробно изучим загрузчики, их возможности, принцип работы, а также узнаем, как их настроить. В первой части мы познакомились с webpack, узнали, что это такое, а также изучили некоторые его возможности. Мы написали скрипт, подключили модуль к нему и создали пакет с помощью webpack. Самое важное из всего этого было то, что мы запаковали несколько модулей. Теперь пора понять, что модуль может ссылаться не только на JS файлы. По умолчанию в webpack поддерживаются только JS модули. Но что делать, если нам нужно запаковать в выходной файл CSS? Что если нам нужно взять HTML шаблоны из файлов верстки и использовать их в скрипте? Знакомьтесь с загрузчиками.
Что такое загрузчик?
Из документации: «Загрузчики представляют собой трансформации, применяемые к исходному файлу вашего приложения. Это функции (под управлением node.js), берущие исходные данные из файлов в качестве параметров и возвращающие новые исходники.»
Другими словами, с помощью загрузчиков мы можем сказать браузеру, чтобы он применил трансформации к файлам определенного типа и загрузил их в выходной файл. Например, если мы использовали CSS загрузчик, мы можем подключить CSS файл в любой файл JS. Потом за дело берется webpack, трансформирует это в интерпретируемый JS код и возвращает пакет. Для webpack есть масса готовых загрузчиков. Разберем парочку из них на реальных примерах и исследуем выходные файлы.
Синтаксис настроек загрузчика
Нам понадобится новый ключ module в файле настроек. В нем мы будем хранить все наши загрузчики в виде массива с ключом loaders. Наш код будет строиться на примере из первой части, скопируйте файлы. Теперь наш файл должен выглядеть так:
1 2 3 4 5 6 7 8 9 10 |
module.exports = { entry: path.join(__dirname, 'src/index.js'), output: { path: path.join(__dirname, 'dist'), filename: 'index.bundle.js' }, module: { loaders: [] } }; |
Не вдаваясь в подробности, все загрузчики представлены в виде объектов с тестом на регулярное выражение. Далее идет строка или массив применяемых загрузчиков. Например:
1 2 3 4 5 6 7 8 9 10 |
loaders: [ { test: /\.extension$/, loader: 'some-loader' }, { test: /\.differentextension$/, loaders: ['some-loader', 'another-loader'] } ] |
Особый интерес вызывает второй загрузчик. Здесь мы сцепили несколько загрузчиков. Иногда это необходимо и очень полезно. Добавим наш первый загрузчик и вставим его в тест.
Стандартные шаблоны с HTML загрузчиком
Представим ситуацию: у нас есть простое JS приложение, в основе которого лежит некое взаимодействие с пользователем, а нам нужно показать HTML шаблон. Чтобы не складывать все в одну корзину, мы хотим, чтобы наш HTML хранился в отдельном файле, но по требованию мог подключаться и запаковываться. HTML loader – идеальный кандидат. Давайте установим его:
1 |
npm install html-loader --save-dev |
Переходим в настройки webpack, добавляем загрузчик:
1 2 3 4 |
{ test: /\.html$/, loader: 'html' } |
Создаем HTML шаблон в src/hello.html:
1 2 3 |
<h1>Hello!</h1> <p>Welcome to this awesome application. I hope you enjoy it.</p> <button>More awesome</button> |
Переходим в файл точку входа src/index.js, удаляем старый код. Посмотрим, что произойдет, если подключить HTML шаблон и вывести логи в консоль:
1 2 3 |
var hello = require('./hello.html'); console.log(hello); |
Создаем пакет с помощью команды:
1 |
npm run build |
Откроем файл dist/index.bundle.js, давайте посмотрим, что в нем. Прокручиваем до первого модуля. Видим, что результат __webpack_require__(1) присвоен переменной hello. Во втором модуле (с индексом 1 в массиве) загрузчик применил правильные трансформации к HTML шаблону и конвертировал его в строку с новыми символами. Давайте сами посмотрим на это, запустив пакет в node:
1 2 3 4 5 6 7 |
node dist/index.bundle.js output: <h1>Hello!</h1> <p>Welcome to this awesome application. I hope you enjoy it.</p> <button>More awesome</button> |
Если мы используем HTML, нам нужно как-то включить его в DOM. Давайте займемся этим, создадим стандартную веб-страницу:
1 |
touch index.html |
В файле пропишем стандартный HTML5 код, добавим тег div для нашего HTML, а также подключим наш запакованный JS:
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Cooking With Webpack Part 2 - Using Loaders</title> </head> <body> <div id="app"></div> <script src="dist/index.bundle.js"></script> </body> </html> |
Вернемся в файл src/index.js. Напишем простенький JS код для вставки шаблона в наше приложение при запуске скрипта:
1 2 3 4 |
var hello = require('./hello.html'); var app = document.querySelector('#app'); app.innerHTML = hello; |
Сохраняем, компилируем с помощью npm run build и открываем файл index.html в браузере. Если вы как я радуетесь даже маленьким победам, то вы подскочите со стула. Мы успешно подключили HTML шаблон в наш JS, трансформировав его с помощью загрузчика. С помощью загрузчика мы вставили шаблон в DOM запакованного скрипта.
Вставка стилей с помощью Style и CSS Loader
Приложения и сайты на чистом HTML выглядят скучно. Давайте загрузим стили в наше приложение, чтобы оно выглядело привлекательнее!
Заметка: в следующем примере мы подключим CSS в JS и вставим его напрямую в DOM. По поводу этой методики ведутся горячие споры. С одной стороны, если CSS кода мало, это экономит время на HTTP запросы. С другой стороны, если стилей много, они будут долго обрабатываться. Мы вставим стили прямо в DOM, но в будущих статьях мы познакомимся с плагинами и узнаем, как с помощью webpack возвращать стандартные стили под наши требования.
При работе со стилями стоит рассмотреть два загрузчика:
style loader – вставляет CSS в DOM с помощью тега style;
CSS loader – интерпретирует CSS, резолвит пути и т.д.
Установим оба загрузчика:
1 |
npm install style-loader css-loader --save-dev |
Добавим в настройки webpack загрузчики:
1 2 3 4 |
{ test: /\.css$/, loaders: ['style', 'css'] } |
На этот раз мы использовали массив загрузчиков. Webpack позволяет сцеплять загрузчики с помощью массива с ключом loaders. Создадим первый файл стилей:
1 |
touch src/style.css |
Пропишем в этом файле базовые стили:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
*, *::before, *::after { box-sizing: border-box; } html { font-size: 10px; } body { font-family: sans-serif; font-size: 1.6rem; } |
Переходим в файл точку входа, подключим стили в самом верху:
1 2 3 4 5 6 7 |
// index.js require('./style.css'); var hello = require('./hello.html'); var app = document.querySelector('#app'); app.innerHTML = hello; |
Запаковываем с помощью npm run build и открываем в браузере index.html. Стили должны примениться. Круто! Давайте откроем файл dist/index.bundle.js. Webpack создает еще один модуль для нас, который по необходимости загружается с помощью __webpack_require__. Он трансформировал наши стили в читаемый JS код, пробежался по коду, распарсил его и вставил стили в тег style в тег head дерева DOM. Если открыть вкладку с разметкой в консоли браузера, стили действительно на месте. Замечательно!
Давайте немного отрефакторим наше приложение. Перейдите в файл index.html. Добавим кнопку и контейнер, которые будут отрисовывать приветствие для нас:
1 2 |
<button class="js-add-hello">Add Hello</button> <div id="hellos"></div> |
Закомментируем код в файле hello.js и позволим нашему модулю подключить CSS и вернуть шаблон hello:
1 2 3 4 5 |
require('./hello.css'); module.exports = function() { return require('./hello.html'); }; |
Нам нужно создать файл hello.css, сделаем это:
1 |
touch src/hello.css |
Скопируем в него стили:
1 2 3 4 5 6 7 8 9 10 |
.Hello { margin: 2rem 0; padding: 2rem; border: solid 1px #ddd; } .Hello-title { margin-bottom: 1rem; font-size: 1.6rem; } |
Заменив код в файле hello.html на этот:
1 2 3 4 |
<div class="Hello"> <h3 class="Hello-title">Hello, World!</h3> <p>The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.</p> </div> |
Связываем все вместе в файле index.js:
1 2 3 4 5 6 7 8 9 10 11 12 |
require('./style.css'); var hello = require('./hello.js'); var button = document.querySelector('.js-add-hello'); var hellos = document.querySelector('#hellos'); button.addEventListener('click', function(e) { e.preventDefault(); hellos.insertAdjacentHTML('beforeend', hello()); }); |
Запаковываем все с помощью npm run build и открываем index.html в браузере. Вуаля, наше приложение работает!
Вы можете подумать, что все, что мы сейчас проделали, не имеет ничего общего с webpack. Я не соглашусь. Да, мы могли бы написать все это в JS файле и CSS файле. Но на самом деле мы проверили возможности webpack. Мы все разделили, сгруппировали разметку, стили и логику компонента hello. Webpack позволил нам изолированно работать с компонентом hello, после чего запаковать его с остальными файлами проекта.
Чтобы доказать свою точку зрения, давайте поработаем только с компонентом hello. Чтобы код больше походил на шаблон, давайте использовать handlebars вместо обычного HTML:
1 2 |
npm install handlebars handlebars-loader --save-dev cp src/hello.html src/hello.hbs |
Подключим handlebars трансформации в конфиге:
1 2 3 4 |
{ test: /\.hbs$/, loader: 'handlebars' } |
Добавим переменную name в handlebars шаблоне:
1 2 3 4 |
<div class="Hello"> <h3 class="Hello-title">Hello, {{name}}!</h3> <p>The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.</p> </div> |
Изменим наш модуль hello.js, чтобы он принимал name и рендерил шаблон с этой переменной:
1 2 3 4 5 6 7 8 9 10 11 |
require('./hello.css'); module.exports = function(name) { if (name === undefined) { name = 'World'; } return require('./hello.hbs')({ name: name }); }; |
Если все запаковать и обновить страницу, результат не изменится, а это хорошо! Мы успешно отработали компонент в изоляции от остальных файлов, подключили нужные нам файлы. Отредактируем файл точку входа для лучшего использования компонента. Во-первых, добавим поле ввода в index.html:
1 |
<input type="text" class="Input js-add-hello" placeholder="Type a name and hit enter"> |
И JS:
Запаковываем, обновляем и вуаля. Красота.
Переход на современный JavaScript с помощью Babel Loader
Подводя статью к завершению, я покажу вам еще один загрузчик, с помощью которого можно писать JS следующего поколения с помощью babel. Babel – JS компилятор с множеством настроек. Но мы не будем ничего усложнять. Как обычно, нам понадобится babel loader и несколько других зависимостей, судя по документации:
1 |
npm install babel-loader babel-core babel-preset-es2015 --save-dev |
Пакет babel-loader необходим для фактической загрузки в webpack, babel-core нужен для компиляции JS, а babel-preset-es2015 задает базовые шаблоны. Подключим загрузчик в конфиге:
1 2 3 4 5 6 7 8 |
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015'] } } |
Здесь мы использовали два дополнительных свойства загрузчика:
exclude для исключения из трансформаций на основе регулярного выражения;
query для отправки информации в настройки babel.
Теперь можно поменять весь JS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// index.js import './style.css'; import hello from './hello.js'; const input = document.querySelector('.js-add-hello'); const hellos = document.querySelector('#hellos'); input.addEventListener('keyup', e => { if (e.keyCode === 13) { hellos.insertAdjacentHTML('beforeend', hello(e.target.value)); input.value = ''; } }); // hello.js import './hello.css'; import helloTpl from './hello.hbs'; export default function(name = 'World') { return helloTpl({ name: name }); }; |
Запаковываем, обновляем браузер и бум! Магия. Теперь можно писать JS следующего поколения где угодно, благодаря функционалу webpack по загрузке и трансформации в паре с babel. Удивительно!
Заключение
Ну и ну! На этот раз мы окунулись в мир загрузчиков и посмотрели, как они применяют трансформации к нашим пакетам. Загрузчики имеют высокое значение в среде webpack. Надеюсь, я дал достаточно информации, чтобы вы сами смогли настроить их. Если у вас возникли вопросы, комментарии или хотите связаться со мной, пишите об этом ниже или открывайте обсуждение на GitHub.
Источник: //callmenick.com/
Редакция: Команда webformyself.