Что такое модули
По мере роста нашего приложения, мы обычно хотим разделить его на много файлов, так называемых «модулей». Модуль обычно содержит класс или библиотеку с функциями. Долгое время в JavaScript отсутствовал синтаксис модулей на уровне языка. Это не было проблемой, потому что первые скрипты были маленькими и простыми. В модулях не было необходимости.
Со временем скрипты становились всё более и более сложными, поэтому сообщество придумало несколько вариантов организации кода в модули. Появились библиотеки для динамической подгрузки модулей. Например:
AMD
одна из самых старых модульных систем, изначально реализована библиотекой require.jsCommonJS
модульная система, созданная для сервера Node.jsUMD
ещё одна модульная система, предлагается как универсальная, совместима с AMD и CommonJS
Теперь все они постепенно становятся частью истории, хотя их можно найти в старых скриптах. Система модулей на уровне языка появилась в стандарте JavaScript в 2015 году и постепенно эволюционировала, их называют ES модули. Из-за этого разработанные сообществом модули стремительно устарели и ими пользоваться не стоит.
Обратить внимание стоит лишь на модули CommonJS
, так как на них изначально работал NodeJS
, да и сейчас он работает на них по умолчанию в целях совместимости. Кроме того, часто в инструкциях по подключению различных библиотек вы встретите описание именно CommonJS
варианта, а не ES
. Поэтому вы должны представлять себе, как переделать код CommonJS
на ES
.
Есть еще нюанс, хотя модули теперь закреплены в стандарте JavaScript, просто так они не будут работать в браузере. Для этого нужно использовать один из популярных инструментов сборки.
Что такое модуль
Модуль – это просто файл, один скрипт – это один модуль.
Модули могут загружать друг друга и использовать директивы export
и import
, чтобы обмениваться функциональностью, вызывать функции одного модуля из другого:
export
отмечает переменные и функции, которые должны быть доступны вне текущего модуляimport
позволяет импортировать функциональность из других модулей
У нас есть основной файл:
index.html<!DOCTYPE html>
<html lang="ru">
<head>
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
</body>
<script type="module" src="main.js"></script>
</html>
Файл say.js
, экспортирует функцию из другого файла:
say.jsexport function sayHi() {
let div = document.querySelector("div");
div.innerHTML = "Модульная функция отработана";
}
Другой файл main.js
может импортировать и использовать функцию sayHi()
:
main.jsimport { sayHi } from "./say.js";
let button = document.querySelector("button");
button.addEventListener("click", function () {
sayHi();
});
Директива import
загружает модуль по пути ./say.js
относительно текущего файла и записывает экспортированную функцию sayHi
.
Основные возможности модулей
Чем отличаются модули от «обычных» скриптов? Есть основные возможности и особенности, работающие как в браузере, так и в серверном JavaScript.
Всегда use strict
В модулях всегда используется режим use strict
. Например, присваивание к необъявленной переменной вызовет ошибку:
<script type="module">
a = 5; // ошибка
</script>
Своя область видимости переменных
Каждый модуль имеет свою собственную область видимости. Другими словами, переменные и функции, объявленные в модуле, не видны в других скриптах. Модули должны экспортировать функциональность, предназначенную для использования извне. А другие модули могут её импортировать.
В браузере также существует независимая область видимости для каждого скрипта <script type="module">
:
<script type="module">
// Переменная доступна только в этом модуле
let user = "John";
</script>
<script type="module">
alert(user); // Error: user is not defined
</script>
Если нам нужно сделать глобальную переменную уровня всей страницы, можно явно присвоить её объекту window
, тогда получить значение переменной можно обратившись к window.user
. Но это должно быть исключением, требующим веской причины.
Код в модуле выполняется только один раз при импорте
Если один и тот же модуль используется в нескольких местах, то его код выполнится только один раз, после чего экспортируемая функциональность передаётся всем импортёрам.
import.meta
Объект import.meta
содержит информацию о текущем модуле. Содержимое зависит от окружения. В браузере он содержит ссылку на скрипт или ссылку на текущую веб-страницу, если модуль встроен в HTML:
<script type="module">
alert(import.meta.url); // ссылка на html страницу для встроенного скрипта
</script>
В модуле «this» не определён
Это незначительная особенность, но для полноты картины нам нужно упомянуть об этом. В модуле на верхнем уровне this
не определён (undefined). Сравним с не-модульными скриптами, там this
– глобальный объект:
<script>
alert(this); // window
</script>
<script type="module">
alert(this); // undefined
</script>