Модульность в JavaScript
Учимся организовывать свой код в модули и переиспользовать их.
Модульность, введение.
По мере роста нашего приложения, мы обычно хотим разделить его на много файлов, так называемых «модулей». Модуль обычно содержит класс или библиотеку с функциями.
Долгое время в JavaScript отсутствовал синтаксис модулей на уровне языка. Это не было проблемой, потому что первые скрипты были маленькими и простыми. В модулях не было необходимости.
Модульность
Модульность
Идея с организацией кода по «модулям» не новая и повсеместно используется в различных языках программирования.
В стандарте JavaScript изначально не было такого понятия как модульность, но со временем скрипты становились всё более и более сложными, поэтому сообщество придумало несколько вариантов организации кода в модули.
Позже появились различные подходы для динамической подгрузки модулей.
Что такое модуль?
Модуль — это переиспользуемая часть кода, содержащая в себе детали реализации и предоставляющая открытое API, что позволяет легко загрузить её и использовать в другом коде.
Зачем нужны модули?
Модули в различных стандартах ES
ES? ES — это просто сокращение для ECMAScript.
ECMAScript — стандарт, а JavaScript — самая популярная реализация этого стандарта.
ES5 – Версия стандарта от 2009 года, в которой сообщество впервые стало изобретать свои подходы по выделению кода в некое подобие модулей.
Давайте взглянем на два из них: �мгновенно вызываемая функция (IIFE) и �выявление модуля (Revealing Module).
Immediately Invoked Function Expression
Немедленно вызываемая функция (IIFE) — анонимная функция, которая вызывается сразу после объявления. Обратите внимание: функция окружена скобками
Immediately Invoked Function Expression
Мгновенно вызываемые функции позволяют нам:
Однако они не дают нам механизма управления зависимостями.
Паттерн выявления модуля
Паттерн выявления модуля
Паттерн выявления модуля предоставляет те же преимущества, что и IIFE, �но опять же не даёт возможности управлять зависимостями.
Форматы модулей
С развитием JavaScript появлялись разные синтаксические возможности определения модулей, и у каждого были свои сильные и слабые стороны.
Формат модуля — это синтаксис, который используется для его определения.
До создания ECMAScript 6, или ES2015, в JavaScript не было официального синтаксиса для определения модулей. А значит, опытные разработчики предлагали разные форматы определения.
Форматы модулей
Вот несколько наиболее известных и широко используемых:
Давайте рассмотрим каждый из них, чтобы вы смогли распознать их по синтаксису.
Asynchronous module definition
AMD это подход к разработке, при котором модули и их зависимости могут быть загружены асинхронно. Асинхронная загрузка модулей позволяет улучшить скорость загрузки веб-страницы в целом, так как модули загружаются одновременно с остальным контентом сайта.
CommonJS
Универсальное определение модуля (UMD)
Фактически у нас было два стандарта, которые не могли ужиться друг с другом. AMD-модули без модификации кода нельзя было задействовать в средах, реализующих спецификацию CommonJS Modules (Node.js), а модули CommonJS не удавалось использовать с инструментами, поддерживающими AMD: RequireJS, curl.js. Да, впоследствии появилась возможность использовать RequireJS для работы с CommonJS-модулями, но такое положение дел всё равно никого не устраивало. Именно для решения проблемы переносимости кода между разными системами модульности и был разработан паттерн UMD — Universal Module Definition.
В сердце такой реализации паттерна находится самовызывающаяся функция, аргумент которой принимает разные значения в зависимости от окружения. В качестве аргумента передаётся функция следующего вида:
Это если код используется как CommonJS-модуль. Если же код используется как AMD-модуль, в качестве аргумента передаётся функция define. Адаптация кода под различные окружения происходит как раз благодаря такой подмене.
System.register
Формат System.register был разработан для поддержки синтаксиса модулей ES6 в ES5:
Формат модулей ES6+
В ES6 JavaScript уже поддерживает нативный формат модулей.
Он использует токен export для экспорта публичного API модуля:
Формат модулей
Загрузчики модулей
Загрузчик модулей интерпретирует и загружает модуль, написанный в определённом формате.
Загрузчик модуля запускается в среде исполнения:
Загрузчики модулей
Если вы откроете вкладку «Сеть» в консоли разработчика на своём браузере, вы увидите, что многие файлы были загружены по запросу загрузчика модулей.
Вот несколько популярных загрузчиков под ES6:
Сборщики модулей
Сборщики модулей
Сборщик модулей заменяет собой загрузчик модулей. Однако в отличие от загрузчика модулей, сборщик модулей запускается при сборке:
вы запускаете сборщик модулей для создания файла пакета во время сборки (например, bundle.js);
и загружаете пакет в браузер.
Если вы откроете вкладку «Сеть» в консоли разработчика на своём браузере, вы увидите, что загружен только один файл. Таким образом, отпадает необходимость в загрузчике модулей: весь код включён в один пакет.
WebPack
Вебпак — это сборщик модулей. Он анализирует модули приложения, создает граф зависимостей, затем собирает модули в правильном порядке в один или более бандл (bundle), на который может ссылаться файл «index.html»
Какие проблемы решает вебпак?
Какие проблемы решает вебпак?
Подключение таким образом не только утомительно, но и подвержено ошибкам. Важно не только не забыть про какой-нибудь скрипт, но и расположить их в правильном порядке. Если загрузить скрипт, зависящий от React, до загрузки самого React, приложение сломается. Вебпак решает эти задачи. Не нужно беспокоиться о последовательном включении всех скриптов.
Как установить WebPack?
Как установить WebPack?
Для того чтобы установить WebPack в вашей системе глобально необходимо выполнить команду:
npm install webpack --global
Как установить WebPack?
Отлично, WebPack есть, осталось добавить CLI для работы с ним, для этого выполняем:
npm install webpack-cli --global
WebPack configuration
После установки указанных пакетов, вебпак нужно настроить.
Для этого создается файл webpack.config.js, который экспортирует объект. Этот объект содержит настройки вебпака.
Основной задачей вебпака является анализ модулей, их опциональное преобразование и интеллектуальное объединение в один или более бандл, поэтому вебпаку нужно знать три вещи:
WebPack configuration
Если мы сообщим вебпаку путь до этого файла, он использует его для создания графа зависимостей приложения. Для этого необходимо добавить свойство entry в настройки вебпака со значением пути к главному файлу:
module.exports = { � entry: ‘./src/index.js’�}
WebPack configuration
Если мы сообщим вебпаку путь до этого файла, он использует его для создания графа зависимостей приложения. Для этого необходимо добавить свойство entry в настройки вебпака со значением пути к главному файлу:
module.exports = { � entry: ‘./src/index.js’�}
WebPack configuration
После добавления точки входа, нужно сообщить вебпаку о преобразованиях, которые необходимо выполнить перед генерацией бандла. Для этого используются лоадеры.
По умолчанию при создании графика зависимостей на основе операторов import / require() вебпак способен обрабатывать только JavaScript и JSON-файлы.
Для наших лабораторных этого достаточно, кофиг без изменений
module.exports = { � entry: ‘./src/index.js’�}
WebPack configuration
Теперь вебпак знает о точке входа и лоадерах. Следующим шагом является указание директории для бандла. Для этого нужно добавить свойство output в настройки вебпака.
module.exports = { � entry: ‘./src/index.js’,
output: { � path: path.resolve(__dirname, 'dist’), � filename: 'bundle.js’� }�}
WebPack workflow
Весь процесс выглядит примерно так:
Запуск вебпака
На данный момент мы знаем, как работает вебпак и как его настраивать, осталось его запустить.
Для этого необходимо выполнить в директории с конфигурацией комманду webpack build
Подключение модулей/bundle’ов
Для того чтобы модули корректно подключались необходимо тэгу <script> добавить атрибут type=“module”.
Какие преимущества это даёт:
Подключение модулей/bundle’ов
<!doctype html>
<body>
<script type="module">
alert(typeof button); // object: скрипт может 'видеть' кнопку под ним
// так как модули являются отложенными, � то скрипт начнёт выполнятся только после полной загрузки страницы
</script>
�Сравните с обычным скриптом ниже:
�<script>
alert(typeof button); // Ошибка: кнопка не определена, скрипт не видит элементы под ним
// обычные скрипты запускаются сразу, не дожидаясь полной загрузки страницы
</script>
�<button id="button">Кнопка</button>
</body>