Webpack на практике: с нуля до создания автотестов
Это руководство поможет создать сборку для небольшого веб-приложения, а затем научит вас использовать Webpack для настройки автоматических тестов.
57К открытий58К показов
Язык JavaScript повсеместно используется для создания больших веб-сервисов. Для таких проектов приходится импортировать много стороннего кода (Lodash, React, Angular и др.). Из-за этого код усложняется, и в нём гораздо чаще возникают ошибки. Чем больше в вашем коде будет зависимостей, тем большей головной болью станет подключение тегов <script>
в правильном порядке.
Webpack создаёт граф зависимостей для JavaScript, CSS и прочих, выдавая однофайловые сборки кода так, чтобы вы могли импортировать все необходимые ресурсы JavaScript всего одним тегом <script>
.
Это руководство поможет создать сборку для небольшого веб-приложения, а затем научит вас использовать Webpack для настройки автоматических тестов.
Создание приложения
В качестве тестового приложения мы сделаем карту для ленивцев, которая поможет найти магазины в Кембридже, где продают травяной чай из гибискуса. Потому что каждый ленивец в кембриджском заповеднике Fresh Pond знает, что чай из гибискуса — лучший чай, чтобы умерить свой горячий темперамент!
%save-sc0%Создайте каталог с именем webpack-mocha-tutorial
, в него добавьте другой каталог app/src
и запустите пакетный менеджер npm init
или yarn init
. Исходный код приложения находится здесь. Также в статье будут ссылки на коммиты, чтобы прослеживать изменения кода по ходу чтения.
Основная структура приложения будет выглядеть так:
- У вас есть файл
app/src/distance.js
, экспортирующий функцию, которая запускает формулу вычисления расстояния (на самом деле нужно использовать ортодому) и функцию, которая сообщает, какая точка из массива точек ближе всего находится.
- Также у вас есть файл
app/src/page.js
, который использует код изdistance.js
, чтобы вывести ближайший магазин из списка, а затем отобразить его на странице.
- И, наконец, у вас есть страница
index.html
.
Общая структура каталогов такова:
Таким образом, файл distance.js
определяет функции расстояния, затем файл page.js
запускает их, помещая результат функции sortByDistance()
в дерево документов (DOM). Но если посмотреть на зависимость между файлами, то видно, что файл page.js
зависит от файла distance.js
, а не наоборот (Commit 2).
Добавляем Webpack
Для работы с Webpack необходимо его установить с помощью npm или yarn:
Теперь у вас подключён Webpack и доступна его командная строка. Но прежде, чем можно будет запустить сборку, файл page.js
должен импортировать код из distance.js
. А distance.js
экспортирует свои функции с помощью строки:
И чтобы page.js
мог использовать экспортированную функцию sortByDistance()
, добавляем строку:
Отлично, теперь все зависимости JavaScript связаны. Используем Webpack для создания приложения. Выполним следующую команду:
Сейчас вы должны увидеть новый файл dist/main.js
, который содержит весь ваш код из page.js
и distance.js
. Далее получаем index.html
с импортом dist/main.js
вместо всех скриптов из app/src
, изменив код страницы следующим образом:
Теперь можете открыть страницу в браузере, код по-прежнему должен работать. Поскольку файл main.js
содержит весь код из distance.js
и page.js
, можно импортировать всё из одного файла.
Это работает так: с помощью команды $ npx webpack app/src/page.js
вы указываете, что отправной точкой (в терминологии Webpack — точкой входа в ваш код JavaScript) является page.js
. Webpack читает файл page.js
и видит в нём строку import {sortByDistance} from ./distance
. Теперь он знает, что distance.js
является зависимостью к page.js
. И из всех зависимостей в вашем коде Webpack строит граф и использует его для построения пакетного JavaScript-файла dist/main.js
(Commit 3).
Webpack строит граф зависимостей из точки входа, page.js
Между прочим, это также работает, когда ваш код импортирует сторонние зависимости в каталог node_modules
Прим. перев.: все модули, используемые в npm, по умолчанию подключаются из директории node_modules
.
Давайте попробуем выполнить некоторые манипуляции с графом зависимостей с помощью jQuery вместо document.getElementById()
. Сначала установим jQuery:
$ yarn add --dev jquery
Затем обновим page.js
, чтобы включить в него jQuery:
Теперь граф зависимостей выглядит так:
Ваш новый граф зависимостей, где page.js включает jQuery
И если выполнить $ npx webpack app/src/page.js
и перезагрузить index.html
(несмотря на то, что размер файла dist/main.js
намного больше из-за кода jQuery) приложение по-прежнему работает.
Прежде чем продолжить, перейдите в файл package.json
и добавьте эти три строчки:
Теперь, чтобы запустить сборку пакета, можно просто выполнить $ yarn build
вместо $ npx webpack app/src/page.js
. Если команда для сборки изменится, будет удобнее просто обновить её в файле package.json
с помощью новой команды сборки и вы по-прежнему можете выполнять сборку с помощью $ yarn build
, вместо того чтобы привыкать к запуску новой команды (Commit 4).
Настройка Webpack с помощью файла webpack.config.js
То, что можно сделать с помощью команды $ npx webpack app/src/page.js
, является стандартным режимом работы Webpack. Если выполнить $ webpack [entry-file.js]
, то он создаст граф зависимостей из входного файла и выдаст пакетный файл dist/main.js
. Но задать расположение точек входа и выхода программы можно, настроив файл конфигурации. Поместите следующий код в файл с именем webpack.config.js
:
Теперь можно выполнить $ npx webpack
или сделать ту же сборку, что и раньше, без указания точки входа в программу в аргументах командной строки, т. к. теперь всё это есть в webpack.config.js
. Также это значит, что нужно обновить скрипт файла package.json
следующим образом:
Если в файле конфигурации изменить путь вывода на что-то вроде __dirname + "/somewhere_else"
, то при повторном выполнении команды $ yarn build
пакетный файл будет помещён в somewhere_else/main.js
(Commit 5).
Файл конфигурации предназначен не только для настройки расположения входных и выходных файлов. Также можно настроить что именно Webpack делает, когда встречает файлы разных типов, используя специальные загрузчики, которые на самом деле являются JavaScript-программами, преобразующими ваш код. Например, в файле конфигурации может быть правило, определяющее, что, если Webpack встречает файл TypeScript в своём графе зависимостей, этот файл отправляется в загрузчик, который преобразует его из TypeScript в обычный JavaScript.
Загрузчик, который будет использоваться далее, — это Babel. Если вы не использовали его раньше, Babel — это инструмент, который берёт код JS, использующий современные функции, и преобразует его в эквивалент, совместимый со старыми версиями JavaScript. Это позволяет вашему приложению работать в старых браузерах или в браузерах, которые ещё не поддерживают некоторые новые функции JavaScript. В конце концов, некоторые ленивцы не обновляли свои браузеры с 2009 года. Некоторая часть написанного кода не будет работать в браузере 2009 года:
Здесь используется стрелочная функция и старые браузеры её не воспринимают. Поэтому используем загрузчик и отправим эту функцию в прошлое. Для начала выполним следующее:
Затем в файле webpack.config.js
добавим следующий код в module.exports
:
Этот код добавит новое правило в ваш Webpack. Если в дереве зависимостей Webpack встречает файл, который заканчивается на .js
(например, distance.js
), и этот файл отсутствует в папке node_modules
(например, jQuery), то к этому файлу применяется данное правило.
Любой файл, который соответствует этому правилу, проходит через все загрузчики в блоке use
(в данном случае только через загрузчик babel). Таким образом, файлы distance.js
и page.js
пройдут через загрузчик, что приведёт к удалению стрелочной функции в distance.js
, а затем Webpack продолжит свой путь сборки. Тем временем, как только Webpack встречает jQuery, он просто загружает этот код как он есть без загрузчиков, поскольку jQuery находится в каталоге node_modules
.
Теперь, если выполнить $ yarn build
и посмотреть исходный код в dist/main.js
, фрагмент, соответствующий вашей функции сортировки, использует ключевое слово function
, а не стрелочную функцию (Commit 6).
Подсвеченный код на верхнем изображении — это функция sortByDistance()
из dist/main.js
до использования загрузчика, а подсвеченный код внизу — та же функция после добавления загрузчика. Обратите внимание на то, как выше мы используем стрелочную функцию, а ниже присутствует уже знакомое браузеру 2009 года ключевое слово function
.
Теперь приложение готово к работе на устаревших браузерах. Чтобы этот код был поддерживаемым, следует написать несколько тестов для него.
Добавление тестовых сценариев в сборку
Добавим несколько тестовых сценариев в файл distance.js
. Для этого будем использовать Mocha, пакетный инструмент для написания тестов, и Chai в качестве нашей библиотеки установок. Выполните следующую команду:
Затем создайте новый каталог app/test
и новый файл app/test/distance.test.js
, содержащий следующий фрагмент:
Теперь у вас есть тестовые сценарии для функций distance()
и sortByDistance()
, устанавливающие, что distance()
вычисляет формулу расстояния, а sortByDistance()
сортирует массивы координат с помощью формулы расстояния, используя наборы тестов Mocha и установки Chai. Довольно стандартная тестовая настройка.
Однако, если выполнить $ mocha app/test/distance.test.js
, будет ошибка “Код JavaScript недопустим”, потому что он содержит ключевое слово import
, которое Node в данный момент не поддерживает. Но что если обойти это ограничение, используя Webpack для управления зависимостями тестового кода?
Прим.: это можно легко исправить, просто используя require
вместо import
в тестовых файлах. Но тестовый код также будет проходить через процесс сборки, если вы тестируете JavaScript-код типа Flow, который использует аннотации, или веб-приложения Vue.js, которые используют файлы .vue
. Все они должны быть преобразованы в обычный JavaScript.
Список тестовых инструкций:
- Webpack строит граф зависимостей, начинающийся с тестовых файлов, а не с файлов приложения.
- Webpack создаёт файл JavaScript, содержащий весь тестовый код и его зависимости без ключевого слова
import
. - Выполняются тесты, запуская Mocha для этого JavaScript-файла.
Всё это будет выглядеть следующим образом:
Как можно увидеть, будут происходить две отдельные сборки. Одна из которых содержит код приложения в качестве точки входа и папку dist
в качестве выходной директории, а другая — тестовые файлы в качестве точки входа и папку test-dist
в качестве выходного каталога. Итак, давайте обновим конфигурационный файл, чтобы Webpack поддерживал вторую сборку:
Давайте разберёмся, что этот код делает. В строке 4 есть оператор if
, который выполняется, если системная переменная TESTBUILD
имеет непустое значение. Так что, если вы выполните $ TESTBUILD=true webpack
, то вам придётся вводить оператор if
, но это не потребуется, если просто выполнить $ npx webpack
.
Внутри оператора if
происходит выбор, какой JS-файл является точкой входа. Вместо уже установленного выходного каталога dist
будет папка test-dist
. А вместо точки входа app/src/path.js
— массив файлов, соответствующих глобальному выражению app/test/**/*.test.js
. Другими словами, это все файлы, которые находятся в каталоге app/test
и имеют путь, заканчивающийся на .test.js
.
Новая точка входа и выходной путь передаются в объект module.exports
, затем запускается Webpack для создания тестовой сборки. Конфигурация Webpack представляет собой обычный код JavaScript, поэтому можно использовать стандартную библиотеку Node и операторы if
для её настройки. Если выполнить $ TESTBUILD=true npx webpack
, то можно увидеть каталог test-dist
. А если запустить $ npx mocha test-dist/main.js
, то можно увидеть, как выполняются ваши тесты.
Наконец, в разделе scripts
вашего package.json
добавьте следующую строку:
Это означает, что когда вы выполняете $ yarn test
, создаётся каталог test-dist
(каталог сборки тестирования) с помощью Webpack, затем запускается Mocha для этой сборки, и, наконец, код $ rm -rf test-dist
удаляет каталог test-dist
, поскольку он больше не используется (Commit 7).
Маппинг исходных файлов тестового кода
Тестовая сборка готова, но есть один нюанс, касающийся тестирования кода. Если запустить Mocha для файла test-dist/main.js
и один из этих тестов не пройдёт, как это будет выглядеть? Давайте сделаем тест формулы расстояния в app/test/distance.test.js
ошибочным:
Выполните $ yarn test
и вот что получится:
Тест провален, но нельзя узнать, в какой строке исходного кода находится ошибка. Если таких тестов для веб-приложения у вас много, то поиски строки с ошибкой затянутся надолго.
Код с ошибкой находится в строке 8 в файле app/test/distance.test.js
, но Mocha запускается для файла test-dist/main.js
, поэтому, с точки зрения Mocha, ошибка находится в строке 116. К счастью, Webpack поддерживает source maps, которые покажут, какая строка кода соответствует ошибке. Source maps (или карты кода) — это файлы исходного кода, которые показывают точное соответствие элементов готового рабочего кода проекта и вашего кода разработки. Выполняется своего рода проход декодером по пакетному файлу main.js
, чтобы получить исходные строки кода, которые соответствуют связному коду. Давайте обновим оператор if
в файле webpack.config.js
:
Затем в объект module.exports
добавим строку:
Теперь в тестировочных сборках каталог test-dist
будет содержать файл типа source maps. Выполните $ npx webpack TESTBUILD=true
и каталог test-dist
будет содержать файл main.js.map
в дополнение к пакету main.js
.
Чтобы Mocha мог использовать эту source map при запуске тестов, необходимо установить ещё один пакет:
Теперь, чтобы его использовать, нужно обновить скрипт Mocha в разделе scripts.test
файла package.json
:
Флаг --require source-map-support/register
требует пакет source-map-support
, это означает, что Mocha будет использовать source map, если она доступна. Теперь, если вы выполните $ yarn test
и получите ошибку, вы увидите, в какой строке она находится, и сможете исправить код (Commit 8).
Итак, теперь у вас есть пример настройки обычных и тестовых сборок с использованием маппинга. Существует также множество других способов, которыми вы можете воспользоваться в своих сборках, например, объединение нескольких загрузчиков JavaScript для обработки вашего кода как на конвейере или же запуск Webpack как отладочного сервера, чтобы моментально видеть, как изменения в коде влияют на окончательную сборку Webpack. Продолжайте пробовать различные пакеты для компоновки файла webpack.config.js
!
Для более точной настройки Webpack вам может быть полезен следующий материал:
57К открытий58К показов