1 of 42

Модульность в JavaScript

Учимся организовывать свой код в модули и переиспользовать их.

2 of 42

Модульность, введение.

По мере роста нашего приложения, мы обычно хотим разделить его на много файлов, так называемых «модулей». Модуль обычно содержит класс или библиотеку с функциями.

Долгое время в JavaScript отсутствовал синтаксис модулей на уровне языка. Это не было проблемой, потому что первые скрипты были маленькими и простыми. В модулях не было необходимости.

3 of 42

Модульность

4 of 42

Модульность

Идея с организацией кода по «модулям» не новая и повсеместно используется в различных языках программирования.

В стандарте JavaScript изначально не было такого понятия как модульность, но со временем скрипты становились всё более и более сложными, поэтому сообщество придумало несколько вариантов организации кода в модули.

Позже появились различные подходы для динамической подгрузки модулей.

5 of 42

Что такое модуль?

Модуль — это переиспользуемая часть кода, содержащая в себе детали реализации и предоставляющая открытое API, что позволяет легко загрузить её и использовать в другом коде.

6 of 42

Зачем нужны модули?

  • абстрагировать код, передавая функциональные возможности сторонним библиотекам, так что нам не придётся разбираться во всех сложностях их реализации;
  • инкапсулировать код, скрывая его внутри модуля, если не хотим, чтобы его изменяли;
  • переиспользовать код, избавляясь от необходимости писать одно и то же снова и снова;
  • управлять зависимостями, легко изменяя зависимости без необходимости переписывать наш код.

7 of 42

Модули в различных стандартах ES

ES? ES — это просто сокращение для ECMAScript.

ECMAScript — стандарт, а JavaScript — самая популярная реализация этого стандарта.

ES5 – Версия стандарта от 2009 года, в которой сообщество впервые стало изобретать свои подходы по выделению кода в некое подобие модулей.

Давайте взглянем на два из них: �мгновенно вызываемая функция (IIFE) и �выявление модуля (Revealing Module).

8 of 42

Immediately Invoked Function Expression

Немедленно вызываемая функция (IIFE) — анонимная функция, которая вызывается сразу после объявления. Обратите внимание: функция окружена скобками

9 of 42

Immediately Invoked Function Expression

Мгновенно вызываемые функции позволяют нам:

  • полностью инкапсулировать код в IIFE, так что нам не придётся разбираться, как работает код IIFE;
  • определять переменные внутри IIFE, чтобы они не засоряли глобальную область видимости (переменные, объявленные внутри IIFE, остаются в рамках замкнутого выражения).

Однако они не дают нам механизма управления зависимостями.

10 of 42

Паттерн выявления модуля

11 of 42

Паттерн выявления модуля

Паттерн выявления модуля предоставляет те же преимущества, что и IIFE, �но опять же не даёт возможности управлять зависимостями.

12 of 42

Форматы модулей

С развитием JavaScript появлялись разные синтаксические возможности определения модулей, и у каждого были свои сильные и слабые стороны.

Формат модуля — это синтаксис, который используется для его определения.

До создания ECMAScript 6, или ES2015, в JavaScript не было официального синтаксиса для определения модулей. А значит, опытные разработчики предлагали разные форматы определения.

13 of 42

Форматы модулей

Вот несколько наиболее известных и широко используемых:

  • асинхронное определение модуля (Asynchronous Module Definition или AMD);
  • CommonJS;
  • универсальное определение модуля (Universal Module Definition или UMD);
  • System.register;
  • формат модуля ES6.

Давайте рассмотрим каждый из них, чтобы вы смогли распознать их по синтаксису.

14 of 42

Asynchronous module definition

AMD это подход к разработке, при котором модули и их зависимости могут быть загружены асинхронно. Асинхронная загрузка модулей позволяет улучшить скорость загрузки веб-страницы в целом, так как модули загружаются одновременно с остальным контентом сайта.

15 of 42

CommonJS

  • CommonJS — это стиль, с которым вы можете быть знакомы, если вы пишите что-нибудь под NodeJs. Модули в подобном стиле можно использовать и на стороне клиента в браузере при помощи таких библиотек как Browserify.

16 of 42

Универсальное определение модуля (UMD)

Фактически у нас было два стандарта, которые не могли ужиться друг с другом. AMD-модули без модификации кода нельзя было задействовать в средах, реализующих спецификацию CommonJS Modules (Node.js), а модули CommonJS не удавалось использовать с инструментами, поддерживающими AMD: RequireJS, curl.js. Да, впоследствии появилась возможность использовать RequireJS для работы с CommonJS-модулями, но такое положение дел всё равно никого не устраивало. Именно для решения проблемы переносимости кода между разными системами модульности и был разработан паттерн UMD — Universal Module Definition.

17 of 42

18 of 42

В сердце такой реализации паттерна находится самовызывающаяся функция, аргумент которой принимает разные значения в зависимости от окружения. В качестве аргумента передаётся функция следующего вида:

Это если код используется как CommonJS-модуль. Если же код используется как AMD-модуль, в качестве аргумента передаётся функция define. Адаптация кода под различные окружения происходит как раз благодаря такой подмене.

19 of 42

System.register

Формат System.register был разработан для поддержки синтаксиса модулей ES6 в ES5:

20 of 42

Формат модулей ES6+

В ES6 JavaScript уже поддерживает нативный формат модулей.

Он использует токен export для экспорта публичного API модуля:

21 of 42

22 of 42

Формат модулей

  • Для обеспечения единого формата модулей ES6 сегодня, нам может потребоваться компилятор наподобие Babel, который будет переводить наш код в формат ES5, такой, как AMD или CommonJS, перед тем, как код будет запущен в браузере.

23 of 42

24 of 42

Загрузчики модулей

Загрузчик модулей интерпретирует и загружает модуль, написанный в определённом формате.

Загрузчик модуля запускается в среде исполнения:

  • вы загружаете загрузчик модуля в браузере;
  • вы сообщаете загрузчику, какой главный файл приложения запустить;
  • модуль скачивает и интерпретирует главный файл приложения;
  • загрузчик модулей скачивает файлы по мере необходимости.

25 of 42

Загрузчики модулей

Если вы откроете вкладку «Сеть» в консоли разработчика на своём браузере, вы увидите, что многие файлы были загружены по запросу загрузчика модулей.

Вот несколько популярных загрузчиков под ES6:

  • RequireJS: загрузчик модулей в формате AMD ;
  • SystemJS: загрузчик модулей в форматах AMD, CommonJS, UMD и System.register format.

26 of 42

Сборщики модулей

27 of 42

Сборщики модулей

Сборщик модулей заменяет собой загрузчик модулей. Однако в отличие от загрузчика модулей, сборщик модулей запускается при сборке:

вы запускаете сборщик модулей для создания файла пакета во время сборки (например, bundle.js);

и загружаете пакет в браузер.

Если вы откроете вкладку «Сеть» в консоли разработчика на своём браузере, вы увидите, что загружен только один файл. Таким образом, отпадает необходимость в загрузчике модулей: весь код включён в один пакет.

  • Пара популярных сборщиков:
  • Browserify: сборщик для модулей CommonJS;
  • Webpack: сборщик для модулей AMD, CommonJS, ES6.

28 of 42

WebPack

Вебпак — это сборщик модулей. Он анализирует модули приложения, создает граф зависимостей, затем собирает модули в правильном порядке в один или более бандл (bundle), на который может ссылаться файл «index.html»

29 of 42

Какие проблемы решает вебпак?

30 of 42

Какие проблемы решает вебпак?

Подключение таким образом не только утомительно, но и подвержено ошибкам. Важно не только не забыть про какой-нибудь скрипт, но и расположить их в правильном порядке. Если загрузить скрипт, зависящий от React, до загрузки самого React, приложение сломается. Вебпак решает эти задачи. Не нужно беспокоиться о последовательном включении всех скриптов.

31 of 42

Как установить WebPack?

  • Чтобы использовать этот инструменты (или прочие модули) нам нужна возможность устанавливать их и управлять ими. Для этого создан npm, Node.js Package Manager. �Он устанавливает нужные вам пакеты и предоставляет удобный интерфейс для работы с ними. Но перед тем как начать использовать npm, вам надо установить в своей системе Node.js.

32 of 42

Как установить WebPack?

Для того чтобы установить WebPack в вашей системе глобально необходимо выполнить команду:

npm install webpack --global

33 of 42

Как установить WebPack?

Отлично, WebPack есть, осталось добавить CLI для работы с ним, для этого выполняем:

npm install webpack-cli --global

34 of 42

WebPack configuration

После установки указанных пакетов, вебпак нужно настроить.

Для этого создается файл webpack.config.js, который экспортирует объект. Этот объект содержит настройки вебпака.

Основной задачей вебпака является анализ модулей, их опциональное преобразование и интеллектуальное объединение в один или более бандл, поэтому вебпаку нужно знать три вещи:

  1. Точка входа приложения
  2. Преобразования, которые необходимо выполнить
  3. Место, в которое следует поместить сформированный бандл

35 of 42

WebPack configuration

Если мы сообщим вебпаку путь до этого файла, он использует его для создания графа зависимостей приложения. Для этого необходимо добавить свойство entry в настройки вебпака со значением пути к главному файлу:

module.exports = { � entry: ‘./src/index.js’�}

36 of 42

WebPack configuration

Если мы сообщим вебпаку путь до этого файла, он использует его для создания графа зависимостей приложения. Для этого необходимо добавить свойство entry в настройки вебпака со значением пути к главному файлу:

module.exports = { � entry: ‘./src/index.js’�}

37 of 42

WebPack configuration

После добавления точки входа, нужно сообщить вебпаку о преобразованиях, которые необходимо выполнить перед генерацией бандла. Для этого используются лоадеры.

По умолчанию при создании графика зависимостей на основе операторов import / require() вебпак способен обрабатывать только JavaScript и JSON-файлы.

Для наших лабораторных этого достаточно, кофиг без изменений

module.exports = { � entry: ‘./src/index.js’�}

38 of 42

WebPack configuration

Теперь вебпак знает о точке входа и лоадерах. Следующим шагом является указание директории для бандла. Для этого нужно добавить свойство output в настройки вебпака.

module.exports = { � entry: ‘./src/index.js’,

output: { � path: path.resolve(__dirname, 'dist’), � filename: 'bundle.js’� }}

39 of 42

WebPack workflow

Весь процесс выглядит примерно так:

  1. Вебпак получает точку входа, находящуюся в ./src/index.js
  2. Он анализирует операторы import / require и создает граф зависимостей
  3. Вебпак начинает собирать бандл, преобразовывая код с помощью соответствующих лоадеров
  4. Он собирает бандл и помещает его в dist/bundle.js

40 of 42

Запуск вебпака

На данный момент мы знаем, как работает вебпак и как его настраивать, осталось его запустить.

Для этого необходимо выполнить в директории с конфигурацией комманду webpack build

41 of 42

Подключение модулей/bundle’ов

Для того чтобы модули корректно подключались необходимо тэгу <script> добавить атрибут type=“module”.

Какие преимущества это даёт:

  • загрузка внешних модулей, таких как <script type="module" src="...">, не блокирует обработку HTML.
  • модули, даже если загрузились быстро, ожидают полной загрузки HTML документа, и только затем выполняются.
  • сохраняется относительный порядок скриптов: скрипты, которые идут раньше в документе, выполняются раньше.

42 of 42

Подключение модулей/bundle’ов

<!doctype html>

<body>

<script type="module">

  alert(typeof button); // object: скрипт может 'видеть' кнопку под ним

  // так как модули являются отложенными, � то скрипт начнёт выполнятся только после полной загрузки страницы

</script>

�Сравните с обычным скриптом ниже:

<script>

  alert(typeof button); // Ошибка: кнопка не определена, скрипт не видит элементы под ним

  // обычные скрипты запускаются сразу, не дожидаясь полной загрузки страницы

</script>

<button id="button">Кнопка</button>

</body>