Javascript Модули в браузере — script type=module
В этой заметке поговорим об использовании ES6 модулей в браузере. Это стандартные модули ES6. Только они используются в коде, предназначенном для браузеров.
Чтобы указать браузеру что подключаемый скрипт является модулем, нужно добавить атрибут type="module"
:
<script type="module" src="https://dom/script.js"></script>
Модулем может быть и инлайновый скрипт:
<script type="module"> export const doSomething = message => { console.log('No.'); } </script>
Поддержка браузерами
Главный вопрос - как сейчас поддерживаются модули в бразуере. На сегодня поддержка уже отличная - все основные браузере поддерживают модули. Поэтому уже можно использовать:
Как подключить WordPress скрипт как модуль
Для этого можно воспользоваться хуком script_loader_tag:
add_filter( 'script_loader_tag', 'scripts_as_es6_modules', 10, 3 ); function scripts_as_es6_modules( $tag, $handle, $src ) { if ( 'my-script' === $handle ) { return str_replace( '<script ', '<script type="module"', $tag ); } return $tag; }
Особенности и возможности модулей
Всегда используется «use strict»
Например, присваивание к необъявленной переменной вызовет ошибку.
<script type="module"> a = 5; // ошибка </script>
Отдельная область видимости переменных
Каждый модуль имеет свою собственную область видимости. Другими словами, переменные и функции, объявленные в модуле, не видны в других скриптах:
<!doctype html> <script type="module"> // Переменная доступна только в этом модуле let user = "John"; </script> <script type="module"> alert( user ); // Error: user is not defined </script>
Однако сам модуль, видит переменные родительской области видимости:
<!doctype html> <script> let user = "John"; </script> <script type="module"> alert( user ); //> John </script>
Если нужна глобальная переменная уровня всей страницы, то лучше явно присвоить её объекту window. Но такой подход должен быть исключением, требующим веской причины.
<!doctype html> <script> window.user = "John"; </script> <script type="module"> alert( user ); //> John </script>
Модули должны экспортировать функциональность, предназначенную для использования извне. А другие модули могут её импортировать.
<!doctype html> <script type="module" src="user.js"></script> <script type="module" src="hello.js"></script>
// user.js export let user = "John";
// hello.js import {user} from './user.js';
Код модуля выполняется один раз (при импорте)
Если один и тот же модуль используется в нескольких местах, то его код выполнится только один раз, после чего экспортируемая функциональность передаётся всем импортёрам.
На практике, задача модуля – это, как правило, инициализация – создание внутренних структур данных. А если мы хотим, чтобы что-то можно было использовать много раз, то экспортируем это.
Подробнее тут: https://learn.javascript.ru/modules-intro#kod-v-module-vypolnyaetsya-tolko-odin-raz-pri-importe
import.meta
Объект import.meta
содержит информацию о текущем модуле.
Содержимое зависит от окружения. В браузере он содержит ссылку на скрипт или ссылку на текущую веб-страницу, если модуль встроен в HTML:
<script type="module"> alert(import.meta.url); // ссылка на html страницу для встроенного скрипта </script>
this в модуле не определён
В модуле на верхнем уровне this не определён (undefined):
<script> alert(this); // window </script> <script type="module"> alert(this); // undefined </script>
Особенности в браузерах
Загрузка модуля отложена (deferred)
Модули всегда выполняются в отложенном режиме, так же, как скрипты с атрибутом defer. Это работает для внешних и встроенных скриптов.
Другими словами:
-
Загрузка внешних модулей, таких как
<script type="module" src="...">
, не блокирует обработку HTML. -
Модуль, даже если загрузились быстро, ожидают полной загрузки HTML документа, и только затем выполняются.
- Сохраняется относительный порядок скриптов: скрипты, которые идут раньше в документе, выполняются раньше.
async позволяет не откладывать выполнение модуля и работает для inline скриптов
Для обычных скриптов атрибут async будет работать только если подключается файл - не встроенный скрипт.
Для модулей атрибут async
работает и для файлов и для inline скриптов. А также, он заставляет выполнятся код модуля сразу после загрузки, а не по порядку.
Например скрипт ниже выполнит импорт (загрузит ./analytics.js) и сразу запустится, когда будет готов, даже если HTML документ ещё не будет загружен, или если другие скрипты ещё загружаются.
<script async type="module"> import {counter} from './analytics.js'; counter.count(); </script>
Это очень полезно, когда модуль ни с чем не связан, например для счётчиков, обработчиков событий, рекламы.
Одинаковые внешние скрипты запускаются один раз
Внешние скрипты с одинаковым атрибутом src запускаются только один раз:
<!-- скрипт my.js загрузится и будет выполнен только один раз --> <script type="module" src="my.js"></script> <script type="module" src="my.js"></script>
Внешний скрипт с другого домена, требует CORS
Если модульный скрипт загружается с другого домена, то удалённый сервер должен установить заголовок Access-Control-Allow-Origin
означающий, что загрузка скрипта разрешена.
<!-- another-site.com должен указать заголовок Access-Control-Allow-Origin --> <!-- иначе, скрипт не выполнится --> <script type="module" src="http://another-site.com/their.js"></script>
Это обеспечивает лучшую безопасность по умолчанию.
Не допускаются «голые» модули
В браузере import
должен содержать относительный или абсолютный путь к модулю. Модули без пути называются «голыми» (bare). Они не разрешены в import.
Например, этот import неправильный:
import {sayHi} from 'sayHi'; // Ошибка, "голый" модуль // путь должен быть, например './sayHi.js' или абсолютный
Другие окружения, например Node.js, допускают использование «голых» модулей, без путей, так как в них есть свои правила, как работать с такими модулями и где их искать. Но браузеры пока не поддерживают «голые» модули.
--