В этой статье мы рассмотрим создание настройки сборки для обработки современного JavaScript (запущенного в веб-браузерах) с использованием Babel и webpack .
Это необходимо для обеспечения того, чтобы наш современный код JavaScript, в частности, был совместим с более широким диапазоном браузеров, чем это могло бы быть в противном случае.
JavaScript, как и большинство веб-технологий, постоянно развивается. В старые добрые времена мы могли бы добавить пару тегов <script>
на страницу, может быть, включить jQuery и пару плагинов, и тогда все будет хорошо.
Однако с момента появления ES6 все становится все сложнее. Браузерная поддержка новых языковых функций часто носит неоднозначный характер, и, поскольку приложения JavaScript становятся более амбициозными, разработчики начинают использовать модули для организации своего кода. В свою очередь, это означает, что если вы сегодня пишете современный JavaScript, вам нужно будет ввести шаг сборки в свой процесс.
Как видно из приведенных ниже ссылок, переход с ES6 на ES5 значительно увеличивает количество поддерживаемых нами браузеров.
Цель системы сборки — автоматизировать рабочий процесс, необходимый для подготовки нашего кода для браузеров и производства. Это может включать такие шаги, как преобразование кода в другой стандарт, компиляция Sass в CSS, объединение файлов, минимизация и сжатие кода и многие другие. Чтобы обеспечить их постоянное повторение, необходима система сборки для запуска шагов в известной последовательности из одной команды.
Предпосылки
Чтобы продолжить, вам нужно установить Node.js и npm (они упакованы вместе). Я бы порекомендовал использовать менеджер версий, такой как nvm, для управления установкой Node ( вот как ), и если вам нужна помощь, чтобы разобраться с npm, то ознакомьтесь с удобным для начинающих учебником SitePoint по npm .
Настроить
Создайте корневую папку где-нибудь на вашем компьютере и перейдите в нее из своего терминала / командной строки. Это будет ваша папка <ROOT>
.
Создайте файл package.json
с этим:
npm init -y
Примечание. Флаг -y
создает файл с настройками по умолчанию и означает, что вам не нужно вводить какие-либо обычные детали из командной строки. Они могут быть изменены в вашем редакторе кода позже, если вы хотите.
В папке <ROOT>
сделайте каталоги src
, src/js
и public
. Папка src/js
будет там, где мы разместим наш необработанный исходный код, а общая папка будет там, где закончится переносимый код.
Прозрачный с Вавилоном
Для начала мы собираемся установить babel-cli , которая предоставляет возможность переносить ES6 в ES5, и babel-preset-env , которая позволяет нам ориентироваться на конкретные версии браузера с помощью переданного кода.
npm install babel-cli babel-preset-env --save-dev
Теперь вы должны увидеть следующее в вашем package.json
:
"devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.6.1" }
Пока мы находимся в файле package.json
, давайте изменим раздел scripts
следующим образом:
"scripts": { "build": "babel src -d public" },
Это дает нам возможность вызывать Babel через скрипт, а не напрямую из терминала каждый раз. Если вы хотите узнать больше о скриптах npm и о том, что они могут делать, ознакомьтесь с этим руководством по SitePoint .
Наконец, прежде чем мы сможем проверить, делает ли Babel свое дело, нам нужно создать .babelrc
конфигурации .babelrc
. Это то, что наш пакет babel-preset-env
будет использовать для своих параметров переноса.
Создайте новый файл в каталоге <ROOT>
именем .babelrc
и вставьте в него следующее:
{ "presets": [ [ "env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] } } ] ] }
Это настроит Babel для переноса для последних двух версий каждого браузера, плюс Safari v7 или выше. Другие варианты доступны в зависимости от того, какие браузеры вам нужно поддерживать.
С этим сохранением, мы можем теперь проверить вещи с примером файла JavaScript, который использует ES6. Для целей этой статьи я изменил копию левой панели, чтобы использовать синтаксис ES6 в нескольких местах: шаблонные литералы , функции стрелок , const и let .
"use strict"; function leftPad(str, len, ch) { const cache = [ "", " ", " ", " ", " ", " ", " ", " ", " ", " " ]; str = str + ""; len = len - str.length; if (len <= 0) return str; if (!ch && ch !== 0) ch = " "; ch = ch + ""; if (ch === " " && len < 10) return () => { cache[len] + str; }; let pad = ""; while (true) { if (len & 1) pad += ch; len >>= 1; if (len) ch += ch; else break; } return `${pad}${str}`; }
Сохраните это как src/js/leftpad.js
и из вашего терминала запустите следующее:
npm run build
Если все в порядке, в вашей общей папке вы должны найти новый файл с именем js/leftpad.js
. Если вы откроете это, то обнаружите, что он больше не содержит синтаксиса ES6 и выглядит так:
"use strict"; function leftPad(str, len, ch) { var cache = ["", " ", " ", " ", " ", " ", " ", " ", " ", " "]; str = str + ""; len = len - str.length; if (len <= 0) return str; if (!ch && ch !== 0) ch = " "; ch = ch + ""; if (ch === " " && len < 10) return function () { cache[len] + str; }; var pad = ""; while (true) { if (len & 1) pad += ch; len >>= 1; if (len) ch += ch;else break; } return "" + pad + str; }
Организация вашего кода с помощью модулей ES6
Модуль ES6 — это файл JavaScript, содержащий функции, объекты или значения примитивов, которые вы хотите сделать доступными для другого файла JavaScript. Вы export
из одного, а import
в другой. Любой серьезный современный проект JavaScript должен рассмотреть использование модулей. Они позволяют вам разбить ваш код на автономные блоки и тем самым упростить обслуживание; они помогают вам избежать загрязнения пространства имен; и они помогают сделать ваш код более переносимым и пригодным для повторного использования.
Хотя большая часть синтаксиса ES6 широко доступна в современных браузерах, это не относится к модулям. На момент написания они доступны в Chrome, Safari (включая последнюю версию iOS) и Edge; они скрыты за флагом в Firefox и Opera; и они недоступны (и, вероятно, никогда не будут) в IE11, как и большинство мобильных устройств.
В следующем разделе мы рассмотрим, как мы можем интегрировать модули в нашу сборку.
экспорт
Ключевое слово export — это то, что позволяет нам сделать наши модули ES6 доступными для других файлов, и это дает нам две возможности для этого — именованные и стандартные. С помощью именованного экспорта вы можете иметь несколько экспортов на модуль, а при экспорте по умолчанию у вас есть только один экспорт на модуль. Именованные экспорты особенно полезны, когда вам нужно экспортировать несколько значений. Например, у вас может быть модуль, содержащий ряд служебных функций, которые должны быть доступны в различных местах ваших приложений.
Итак, давайте превратим наш файл leftPad
в модуль, который мы можем затем потребовать во втором файле.
Именованный Экспорт
Чтобы создать именованный экспорт, добавьте следующее в leftPad
файла leftPad
:
export { leftPad };
Мы также можем удалить "use strict";
объявление в верхней части файла, так как модули по умолчанию работают в строгом режиме.
Экспорт по умолчанию
Поскольку в файле leftPad
должна быть экспортирована только одна функция, она может быть хорошим кандидатом для использования export default
:
export default function leftPad(str, len, ch) { ... }
Опять же, вы можете удалить "use strict";
объявление из верхней части файла.
импорт
Чтобы использовать экспортированные модули, теперь нам нужно импортировать их в файл (модуль), в котором мы хотим их использовать.
Для опции export default
экспортированный модуль может быть импортирован под любым именем, которое вы хотите выбрать. Например, модуль leftPad
можно импортировать так:
import leftPad from './leftpad';
Или это может быть импортировано как другое имя, например так:
import pineapple_fritter from './leftpad';
Функционально оба будут работать одинаково, но, очевидно, имеет смысл использовать либо то же имя, под которым оно было экспортировано, либо что-то, что делает импорт понятным — возможно, когда экспортированное имя будет конфликтовать с другим именем переменной, которое уже существует в приемный модуль.
Для указанной опции экспорта мы должны импортировать модуль с тем же именем, под которым он был экспортирован. Для нашего примера модуля мы импортировали бы его так же, как мы использовали в синтаксисе export default
, но в этом случае мы должны заключить импортированное имя в фигурные скобки:
import { leftPad } from './leftpad';
Скобки обязательны для именованного экспорта, и они потерпят неудачу, если они не используются.
При необходимости можно изменить имя именованного экспорта при импорте, и для этого нам нужно немного изменить наш синтаксис, используя синтаксис import [module] as [path]
. Как и в случае с export
, существует множество способов сделать это, каждый из которых подробно описан на странице импорта MDN .
import { leftPad as pineapple_fritter } from './leftpad_es6';
Опять же, изменение имени немного бессмысленно, но оно показывает, что их можно изменить на что угодно. Вы должны всегда придерживаться хороших методов именования, если, конечно, вы не пишете процедуры для приготовления фруктовых рецептов.
Использование экспортируемого модуля
Чтобы использовать экспортированный модуль leftPad
, я создал следующий файл index.js
папке src/js
. Здесь я перебираю массив серийных номеров и добавляю к ним нули, чтобы превратить их в строку из восьми символов. Позже мы воспользуемся этим и опубликуем их в упорядоченном элементе списка на странице HTML. Обратите внимание, что в этом примере используется синтаксис экспорта по умолчанию:
import leftPad from './leftpad'; const serNos = [6934, 23111, 23114, 1001, 211161]; const strSNos = serNos.map(sn => leftPad(sn, 8, '0')); console.log(strSNos);
Как мы делали ранее, запустите скрипт сборки из каталога <ROOT>
:
npm run build
Теперь Babel создаст файл index.js
каталоге public/js
. Как и в случае с нашим файлом leftPad.js
, вы должны увидеть, что Babel заменил весь синтаксис ES6 и оставил позади только синтаксис ES5. Вы также можете заметить, что он преобразовал синтаксис модуля ES6 в Node-based module.exports
, что означает, что мы можем запустить его из командной строки:
node public/js/index.js // [ '00006934', '00023111', '00023114', '00001001', '00211161' ]
Теперь ваш терминал должен выйти из массива строк с префиксом нулей, чтобы они были длиной всего восемь символов. После этого пришло время взглянуть на веб-пакет.
Представляем веб-пакет и интегрируем его с Babel
Как уже упоминалось, модули ES6 позволяют разработчику JavaScript разбивать свой код на управляемые порции, но следствием этого является то, что эти порции необходимо обслуживать запрашивающему браузеру, потенциально добавляя десятки дополнительных HTTP-запросов обратно на сервер — что-то мы действительно должны стараться избегать. Это где веб-пакет приходит.
Веб-пакет является модульным компоновщиком. Его основная цель — обработать ваше приложение путем отслеживания всех его зависимостей, а затем упаковать их все в один или несколько пакетов, которые можно запустить в браузере. Тем не менее, это может быть гораздо больше, в зависимости от того, как он настроен.
Конфигурация webpack основана на четырех ключевых компонентах:
- точка входа
- выходное местоположение
- погрузчики
- плагины
Вступление: содержит начальную точку вашего приложения, откуда веб-пакет может определить его зависимости.
Вывод: указывает, где вы хотите сохранить обработанный пакет.
Загрузчики: это способ преобразования одной вещи в качестве входных данных и генерации чего-то другого в качестве выходных данных. Они могут использоваться для расширения возможностей веб-пакета для обработки не только файлов JavaScript, и, следовательно, для их преобразования в действительные модули.
Плагины: они используются для расширения возможностей веб-пакета в других задачах, помимо пакетирования, таких как минимизация, ограничение и оптимизация.
Чтобы установить веб-пакет, запустите следующее из каталога <ROOT>
:
npm install webpack webpack-cli --save-dev
Это устанавливает webpack локально в проект, а также дает возможность запускать webpack из командной строки через добавление webpack-cli
. Теперь вы должны увидеть веб-пакет в вашем файле package.json
. Пока вы находитесь в этом файле, измените раздел сценариев следующим образом, чтобы теперь он знал, что нужно использовать webpack вместо Babel напрямую:
"scripts": { "build": "webpack --config webpack.config.js" },
Как видите, этот скрипт вызывает файл webpack.config.js
, поэтому давайте создадим его в нашем каталоге <ROOT>
со следующим содержимым:
const path = require("path"); module.exports = { mode: 'development', entry: "./src/js/index.js", output: { path: path.resolve(__dirname, "public"), filename: "bundle.js" } };
Это более или менее простой файл конфигурации, который вам нужен с веб-пакетом. Вы можете видеть, что он использует разделы ввода и вывода, описанные ранее (он может работать только с ними), но также содержит mode: 'development'
настройка mode: 'development'
.
В веб-пакете можно использовать режим «разработки» или «производства». mode: 'development'
настройки mode: 'development'
оптимизирует скорость сборки и отладки, тогда как mode: 'production'
оптимизирует скорость выполнения во время выполнения и размер выходного файла. Хорошее объяснение режимов можно найти в статье Тобиаса Коппера « Webpack 4: режим и оптимизация », если вы хотите узнать больше о том, как их можно настроить, помимо настроек по умолчанию.
Затем удалите все файлы из папки public/js
. Затем повторите это:
npm run build
Вы увидите, что теперь он содержит один файл ./public/bundle.js
. Однако откройте новый файл, и два файла, с которых мы начали, выглядят довольно по-разному. Это раздел файла, который содержит код index.js
. Несмотря на то, что он довольно сильно изменен по сравнению с нашим оригиналом, вы все равно можете выбрать его имена переменных:
/***/ "./src/js/index.js": /*!*************************!*\ !*** ./src/js/index.js ***! \*************************/ /*! no exports provided */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _leftpad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./leftpad */ \"./src/js/leftpad.js\");\n\n\nconst serNos = [6934, 23111, 23114, 1001, 211161];\nconst strSNos = serNos.map(sn => Object(_leftpad__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(sn, 8, '0'));\nconsole.log(strSNos);\n\n\n//# sourceURL=webpack:///./src/js/index.js?"); /***/ }),
Если вы запустите node public/js/bundle.js
из папки <ROOT>
, вы увидите, что вы получите те же результаты, что и ранее.
Transpiling
Как упоминалось ранее, загрузчики позволяют нам преобразовывать одно в другое. В этом случае мы хотим преобразовать ES6 в ES5. Для этого нам понадобится еще пара пакетов:
npm install babel-loader babel-core --save-dev
Чтобы использовать их, webpack.config.js
нужен раздел модуля, добавляемый к нему после раздела вывода , например:
module.exports = { entry: "./src/js/index.js", output: { path: path.resolve(__dirname, "public/js"), filename: "bundle.js" }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: "babel-loader", options: { presets: ["babel-preset-env"] } } } ] } };
При этом используется выражение regex, чтобы идентифицировать файлы JavaScript, которые будут передаваться с помощью babel-loader
, при этом из этого ничего исключая из папки node_modules
. Наконец, babel-loader
должен использовать ранее установленный пакет babel-preset-env
, чтобы установить параметры транспорта, установленные в файле .babelrc
.
Сделав это, вы можете повторить это:
npm run build
Затем проверьте новый public/js/bundle.js
и вы увидите, что все следы синтаксиса ES6 прошли, но он по-прежнему выдает тот же результат, что и ранее.
Внося это в браузер
Создав работающий веб-пакет и настройку Babel, пришло время перенести то, что мы сделали, в браузер. Требуется небольшой HTML-файл, который должен быть создан в папке <ROOT>
как <ROOT>
ниже:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Webpack & Babel Demonstration</title> </head> <body> <main> <h1>Parts List</h1> <ol id="part-list"></ol> </main> <script src="./public/js/bundle.js" charset="utf-8"></script> </body> </html>
В этом нет ничего сложного. Основные моменты, на которые следует обратить внимание: элемент <ol></ol>
, куда будет направлен массив чисел, и элемент <script></script>
непосредственно перед закрывающим </body>
, ссылающийся на ./public/js/bundle.js
Файл ./public/js/bundle.js
. Все идет нормально.
Для отображения списка требуется немного больше JavaScript, поэтому давайте изменим ./src/js/index.js
чтобы это произошло:
import leftPad from './leftpad'; const serNos = [6934, 23111, 23114, 1001, 211161]; const partEl = document.getElementById('part-list'); const strList = serNos.reduce( (acc, element) => acc += `<li>${leftPad(element, 8, '0')}</li>`, '' ); partEl.innerHTML = strList;
Теперь, если вы откроете index.html
в своем браузере, вы должны увидеть упорядоченный список, например:
Продолжая
Как указано выше, наша система сборки практически готова к работе. Теперь мы можем использовать веб-пакет для объединения наших модулей и переноса кода ES6 в ES5 с Babel.
Тем не менее, это немного неприятно, что для переноса нашего кода ES6 мы должны запускать npm run build
каждый раз, когда вносим изменения.
Добавление «часов»
Чтобы устранить необходимость многократного запуска npm run build
, вы можете настроить 'watch'
за вашими файлами и автоматически перекомпилировать webpack каждый раз, когда он обнаруживает изменение в одном из файлов в папке ./src
. Чтобы реализовать это, измените раздел scripts
файла package.json
, как package.json
ниже:
"scripts": { "watch": "webpack --watch", "build": "webpack --config webpack.config.js" },
Чтобы проверить, работает ли он, запустите npm run watch
из терминала, и вы увидите, что он больше не возвращается в командную строку. Теперь вернитесь в src/js/index.js
добавьте дополнительное значение в массив serNos
и сохраните его. Моя теперь выглядит так:
const serNos = [ 6934, 23111, 23114, 1001, 211161, 'abc'];
Если вы сейчас проверите терминал, то увидите, что он вышел из системы и что он повторно запустил задачу build
webpack. И, вернувшись в браузер и обновившись, вы увидите новое значение, добавленное в конец списка, обработанное с помощью leftPad
.
Обновить браузер автоматически
Было бы очень хорошо, если бы мы могли заставить веб-пакет автоматически обновлять браузер каждый раз, когда мы вносим изменения. Давайте сделаем это, установив дополнительный пакет npm под названием webpack-dev-server
. Не забудьте сначала Ctrl + C из задачи watch
!
npm install webpack-dev-server --save-dev
Сделав это, давайте добавим новый скрипт в файл package.json
для вызова нового пакета. Раздел scripts
должен теперь содержать это:
"scripts": { "watch": "webpack --watch", "start": "webpack --watch & webpack-dev-server --open-page 'webpack-dev-server'", "build": "webpack --config webpack.config.js" },
Обратите внимание на --open-page
добавленный в конец скрипта. Это webpack-dev-server
открывать определенную страницу в браузере по умолчанию, используя режим iframe .
Теперь запустите npm start
и вы увидите новую вкладку браузера, открытую по адресу http://localhost:8080/webpack-dev-server/
с отображаемым списком деталей. Чтобы показать, что 'watch'
работает, перейдите в src/js/index.js
и добавьте еще одно новое значение в конец массива serNos
. Когда вы сохраняете свои изменения, вы должны заметить, что они почти сразу же отображаются в браузере.
После этого единственное, что осталось сделать, — это установить режим работы в webpack.config.js
. Как только это будет установлено, webpack также свернет код, который выводит в ./public/js/bundle.js
. Следует отметить, что если mode
не установлен, веб-пакет по умолчанию будет использовать production
конфигурацию.
Вывод
В этой статье вы увидели, как настроить систему сборки для современного JavaScript. Первоначально он использовал Babel из командной строки для преобразования синтаксиса ES6 в ES5. Затем вы увидели, как использовать модули ES6 с ключевыми словами export
и import
, как интегрировать веб-пакет для выполнения задачи связывания и как добавить задачу наблюдения для автоматизации запуска веб-пакета при каждом обнаружении изменений в исходном файле. Наконец, вы увидели, как установить webpack-dev-server
для автоматического обновления страницы каждый раз, когда webpack-dev-server
изменения.
Если вы хотите пойти дальше, я бы посоветовал прочитать глубокое погружение SitePoint в пакет веб-пакетов и модулей , а также изучить дополнительные загрузчики и плагины, которые позволят веб-пакетам выполнять задачи сжатия Sass и ресурсов . Также посмотрите на eslint-loader
и плагин для Prettier .
Удачной упаковки…